| @@ -150,9 +150,9 @@ export default defineConfig({ | |||
| projectName: 'swagger', | |||
| }, | |||
| ], | |||
| mfsu: { | |||
| strategy: 'normal', | |||
| }, | |||
| // mfsu: { | |||
| // strategy: 'normal', | |||
| // }, | |||
| requestRecord: {}, | |||
| icons: {}, | |||
| lessLoader: { | |||
| @@ -221,14 +221,24 @@ export default [ | |||
| component: './ModelDeployment/List', | |||
| }, | |||
| { | |||
| name: '模型部署详情', | |||
| path: 'info/:id', | |||
| component: './ModelDeployment/Info', | |||
| name: '服务详情', | |||
| path: 'serviceInfo/:id', | |||
| component: './ModelDeployment/ServiceInfo', | |||
| }, | |||
| { | |||
| name: '服务版本详情', | |||
| path: 'versionInfo/:id', | |||
| component: './ModelDeployment/VersionInfo', | |||
| }, | |||
| { | |||
| name: '创建推理服务', | |||
| path: 'create', | |||
| component: './ModelDeployment/Create', | |||
| path: 'createService', | |||
| component: './ModelDeployment/CreateService', | |||
| }, | |||
| { | |||
| name: '新增服务版本', | |||
| path: 'addVersion/:id', | |||
| component: './ModelDeployment/CreateVersion', | |||
| }, | |||
| ], | |||
| }, | |||
| @@ -90,7 +90,7 @@ function BasicInfoItemValue({ value, link, url }: BasicInfoItemValueProps) { | |||
| ); | |||
| } else { | |||
| return ( | |||
| <div className="kf-basic-info-item__value kf-basic-info-item__text">{value || '--'}</div> | |||
| <div className="kf-basic-info-item__value kf-basic-info-item__text">{value ?? '--'}</div> | |||
| ); | |||
| } | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| .kf-code-select { | |||
| position: relative; | |||
| display: flex; | |||
| align-items: center; | |||
| &__button { | |||
| position: absolute; | |||
| top: 0; | |||
| left: calc(100% + 10px); | |||
| } | |||
| } | |||
| @@ -0,0 +1,71 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-10-08 15:36:08 | |||
| * @Description: 代码配置选择表单组件 | |||
| */ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import CodeSelectorModal from '@/pages/Pipeline/components/CodeSelectorModal'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| import { Button } from 'antd'; | |||
| import ParameterInput, { type ParameterInputProps } from '../ParameterInput'; | |||
| import './index.less'; | |||
| export { requiredValidator, type ParameterInputObject } from '../ParameterInput'; | |||
| type CodeSelectProps = ParameterInputProps; | |||
| function CodeSelect({ value, onChange, disabled, ...rest }: CodeSelectProps) { | |||
| const selectResource = () => { | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| onOk: (res) => { | |||
| if (res) { | |||
| const { git_url, git_branch, code_repo_name } = res; | |||
| const jsonObj = { | |||
| code_path: git_url, | |||
| branch: git_branch, | |||
| }; | |||
| const jsonObjStr = JSON.stringify(jsonObj); | |||
| const showValue = code_repo_name; | |||
| onChange?.({ | |||
| value: jsonObjStr, | |||
| showValue, | |||
| fromSelect: true, | |||
| ...jsonObj, | |||
| }); | |||
| } else { | |||
| onChange?.({ | |||
| value: undefined, | |||
| showValue: undefined, | |||
| fromSelect: false, | |||
| }); | |||
| } | |||
| close(); | |||
| }, | |||
| }); | |||
| }; | |||
| return ( | |||
| <div className="kf-code-select"> | |||
| <ParameterInput | |||
| {...rest} | |||
| disabled={disabled} | |||
| value={value} | |||
| onChange={onChange} | |||
| onClick={selectResource} | |||
| ></ParameterInput> | |||
| <Button | |||
| className="kf-code-select__button" | |||
| size="large" | |||
| type="link" | |||
| icon={<KFIcon type="icon-xuanzedaimapeizhi" font={16} />} | |||
| disabled={disabled} | |||
| onClick={selectResource} | |||
| > | |||
| 选择代码配置 | |||
| </Button> | |||
| </div> | |||
| ); | |||
| } | |||
| export default CodeSelect; | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 08:42:57 | |||
| * @Description: 参数输入组件 | |||
| */ | |||
| import { CloseOutlined } from '@ant-design/icons'; | |||
| import { Form, Input } from 'antd'; | |||
| import { RuleObject } from 'antd/es/form'; | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 08:42:57 | |||
| * @Description: 参数选择组件 | |||
| */ | |||
| import { PipelineNodeModelParameter } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Select } from 'antd'; | |||
| @@ -1,3 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 数据集、模型、镜像选择表单组件 | |||
| */ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import ResourceSelectorModal, { | |||
| ResourceSelectorResponse, | |||
| @@ -22,7 +28,7 @@ const getSelectBtnIcon = (type: ResourceSelectorType) => { | |||
| return <KFIcon type={selectorTypeConfig[type].buttonIcon} font={16} />; | |||
| }; | |||
| function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) { | |||
| function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) { | |||
| const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | |||
| undefined, | |||
| ); | |||
| @@ -87,6 +93,7 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| <div className="kf-resource-select"> | |||
| <ParameterInput | |||
| {...rest} | |||
| disabled={disabled} | |||
| value={value} | |||
| onChange={onChange} | |||
| onRemove={() => setSelectedResource(undefined)} | |||
| @@ -97,6 +104,7 @@ function ResourceSelect({ type, value, onChange, ...rest }: ResourceSelectProps) | |||
| size="large" | |||
| type="link" | |||
| icon={getSelectBtnIcon(type)} | |||
| disabled={disabled} | |||
| onClick={selectResource} | |||
| > | |||
| {selectorTypeConfig[type].buttontTitle} | |||
| @@ -44,8 +44,8 @@ export enum MirrorVersionStatus { | |||
| Failed = 'failed', // 构建中 | |||
| } | |||
| // 模型部署状态 | |||
| export enum ModelDeploymentStatus { | |||
| // 服务运行状态 | |||
| export enum ServiceRunStatus { | |||
| Init = 'Init', // 启动中 | |||
| Running = 'Running', // 运行中 | |||
| Stopped = 'Stopped', // 已停止 | |||
| @@ -53,14 +53,13 @@ export enum ModelDeploymentStatus { | |||
| Pending = 'Pending', // 挂起中 | |||
| } | |||
| // 模型部署状态选项列表 | |||
| export const modelDeploymentStatusOptions = [ | |||
| { label: '全部', value: '' }, | |||
| { label: '启动中', value: ModelDeploymentStatus.Init }, | |||
| { label: '运行中', value: ModelDeploymentStatus.Running }, | |||
| { label: '已停止', value: ModelDeploymentStatus.Stopped }, | |||
| { label: '失败', value: ModelDeploymentStatus.Failed }, | |||
| { label: '挂起中', value: ModelDeploymentStatus.Pending }, | |||
| // 服务运行状态选项列表 | |||
| export const serviceStatusOptions = [ | |||
| { label: '启动中', value: ServiceRunStatus.Init }, | |||
| { label: '运行中', value: ServiceRunStatus.Running }, | |||
| { label: '已停止', value: ServiceRunStatus.Stopped }, | |||
| { label: '失败', value: ServiceRunStatus.Failed }, | |||
| { label: '挂起中', value: ServiceRunStatus.Pending }, | |||
| ]; | |||
| // 开发环境编辑器状态 | |||
| @@ -71,3 +70,17 @@ export enum DevEditorStatus { | |||
| Failed = 'Failed', // 失败 | |||
| Unknown = 'Unknown', // 未启动 | |||
| } | |||
| export enum ServiceType { | |||
| Video = 'video', | |||
| Image = 'image', | |||
| Audio = 'audio', | |||
| Text = 'text', | |||
| } | |||
| export const serviceTypeOptions = [ | |||
| { label: '视频', value: ServiceType.Video }, | |||
| { label: '图像', value: ServiceType.Image }, | |||
| { label: '音频', value: ServiceType.Audio }, | |||
| { label: '文本', value: ServiceType.Text }, | |||
| ]; | |||
| @@ -80,7 +80,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => { | |||
| }) => { | |||
| const request = config.getInfo; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| if (res && res.data) { | |||
| setInfo(res.data); | |||
| } | |||
| }; | |||
| @@ -85,7 +85,7 @@ const formatSource = (source?: string) => { | |||
| } else if (source === DataSource.HandExport) { | |||
| return '手动导入'; | |||
| } else if (source === DataSource.AtuoExport) { | |||
| return '自动导入'; | |||
| return '实验自动导入'; | |||
| } | |||
| return source; | |||
| }; | |||
| @@ -120,6 +120,7 @@ const getDatasetDatas = (data: DatasetData): BasicInfoData[] => [ | |||
| { | |||
| label: '处理代码', | |||
| value: data.processing_code, | |||
| format: formatProject, | |||
| }, | |||
| { | |||
| label: '数据集分类', | |||
| @@ -160,7 +160,7 @@ export interface DatasetData extends ResourceData { | |||
| resourceType: ResourceType.Dataset; // 用于区别类型 | |||
| data_type?: string; // 数据集分类 | |||
| data_tag?: string; // 研究方向 | |||
| processing_code?: string; // 处理代码 | |||
| processing_code?: ProjectDependency; // 处理代码 | |||
| dataset_source?: string; // 数据来源 | |||
| dataset_version_vos?: ResourceFileData[]; | |||
| } | |||
| @@ -199,15 +199,15 @@ export type ResourceFileData = { | |||
| // 训练任务 | |||
| export type TrainTask = { | |||
| ins_id: number; | |||
| name: string; | |||
| experiment_id: number; | |||
| workflow_id: number; | |||
| ins_id: number; // 实例id | |||
| name: string; // 实验名称 | |||
| experiment_id: number; //实验 id | |||
| workflow_id: number; // 流水线 id | |||
| }; | |||
| // 项目依赖 | |||
| export type ProjectDependency = { | |||
| url: string; | |||
| name: string; | |||
| branch: string; | |||
| url: string; // 项目地址 | |||
| name: string; // 项目名称 | |||
| branch: string; // 分支 | |||
| }; | |||
| @@ -353,7 +353,7 @@ function ExperimentText() { | |||
| fitView: true, | |||
| minZoom: 0.5, | |||
| maxZoom: 5, | |||
| fitViewPadding: 300, | |||
| fitViewPadding: 200, | |||
| modes: { | |||
| default: [ | |||
| // config the shouldBegin for drag-node to avoid node moving while dragging on the anchor-point circles | |||
| @@ -1,3 +1,4 @@ | |||
| import { ResourceType } from '@/pages/Dataset/config'; | |||
| import { getNodeResult } from '@/services/experiment/index.js'; | |||
| import { downLoadZip } from '@/utils/downloadfile'; | |||
| import { openAntdModal } from '@/utils/modal'; | |||
| @@ -34,6 +35,12 @@ function ExperimentResult({ | |||
| }: ExperimentResultProps) { | |||
| const { message } = App.useApp(); | |||
| const [experimentResults, setExperimentResults] = useState<ExperimentResultData[]>([]); | |||
| const resourceType: ResourceType | undefined = pipelineNodeId.startsWith('general-data-process') | |||
| ? ResourceType.Dataset | |||
| : pipelineNodeId.startsWith('model-train') || | |||
| pipelineNodeId.startsWith('distributed-model-train') | |||
| ? ResourceType.Model | |||
| : undefined; | |||
| useEffect(() => { | |||
| getExperimentResult({ id: `${experimentInsId}`, node_id: pipelineNodeId }); | |||
| @@ -42,8 +49,11 @@ function ExperimentResult({ | |||
| // 获取实验结果 | |||
| const getExperimentResult = async (params: any) => { | |||
| const [res] = await to(getNodeResult(params)); | |||
| if (res && res.data) { | |||
| setExperimentResults(res.data); | |||
| if (res && res.data && Array.isArray(res.data)) { | |||
| const data = res.data.filter((item: ExperimentResultData) => item.value.length > 0); | |||
| setExperimentResults(data); | |||
| } else { | |||
| setExperimentResults([]); | |||
| } | |||
| }; | |||
| @@ -52,9 +62,10 @@ function ExperimentResult({ | |||
| downLoadZip(`/api/mmp/minioStorage/download`, { path }); | |||
| }; | |||
| // 导出到模型库 | |||
| const exportToModel = (path: string) => { | |||
| // 导出到数据集、模型 | |||
| const exportToResource = (path: string) => { | |||
| const { close } = openAntdModal(ExportModelModal, { | |||
| resourceType: resourceType!, | |||
| pipelineId, | |||
| experimentId, | |||
| experimentName, | |||
| @@ -86,17 +97,17 @@ function ExperimentResult({ | |||
| > | |||
| 下载 | |||
| </Button> | |||
| <Button | |||
| size="small" | |||
| type="link" | |||
| onClick={() => { | |||
| exportToModel(item.path); | |||
| }} | |||
| > | |||
| 导出到模型库 | |||
| </Button> | |||
| {/* <a style={{ marginRight: '10px' }}>导出到模型库</a> | |||
| <a style={{ marginRight: '10px' }}>导出到数据集</a> */} | |||
| {resourceType && ( | |||
| <Button | |||
| size="small" | |||
| type="link" | |||
| onClick={() => { | |||
| exportToResource(item.path); | |||
| }} | |||
| > | |||
| 导出到{resourceType === ResourceType.Model ? '模型' : '数据集'} | |||
| </Button> | |||
| )} | |||
| </div> | |||
| <div style={{ margin: '15px 0' }} className={styles['experiment-result__item__file']}> | |||
| <span>文件名称</span> | |||
| @@ -1,7 +1,12 @@ | |||
| import editExperimentIcon from '@/assets/img/edit-experiment.png'; | |||
| import KFModal from '@/components/KFModal'; | |||
| import { DataSource, ResourceVersionData, type ResourceData } from '@/pages/Dataset/config'; | |||
| import { addModelVersion, getModelList, getModelVersionList } from '@/services/dataset'; | |||
| import { | |||
| DataSource, | |||
| ResourceType, | |||
| ResourceVersionData, | |||
| resourceConfig, | |||
| type ResourceData, | |||
| } from '@/pages/Dataset/config'; | |||
| import { to } from '@/utils/promise'; | |||
| import { InfoCircleOutlined } from '@ant-design/icons'; | |||
| import { Form, Input, ModalProps, Select } from 'antd'; | |||
| @@ -15,20 +20,8 @@ type FormData = { | |||
| version_desc: string; | |||
| }; | |||
| // type ExportModelResponce = { | |||
| // fileName: string; | |||
| // fileSize: string; | |||
| // url: string; | |||
| // }; | |||
| // type CreateModelVersionParams = FormData & { | |||
| // file_name: string; | |||
| // file_size: string; | |||
| // url: string; | |||
| // // name: string; | |||
| // }; | |||
| interface ExportModelModalProps extends Omit<ModalProps, 'onOk'> { | |||
| resourceType: ResourceType; | |||
| pipelineId: number; // 流水线 id | |||
| experimentId: number; // 实验 id | |||
| experimentName: string; // 实验 name | |||
| @@ -39,6 +32,7 @@ interface ExportModelModalProps extends Omit<ModalProps, 'onOk'> { | |||
| } | |||
| function ExportModelModal({ | |||
| resourceType, | |||
| pipelineId, | |||
| experimentId, | |||
| experimentName, | |||
| @@ -49,9 +43,9 @@ function ExportModelModal({ | |||
| ...rest | |||
| }: ExportModelModalProps) { | |||
| const [form] = Form.useForm(); | |||
| const [models, setModels] = useState<ResourceData[]>([]); | |||
| const [resources, setResources] = useState<ResourceData[]>([]); | |||
| const [versions, setVersions] = useState<ResourceVersionData[]>([]); | |||
| // const [uuid] = useState(Date.now()); | |||
| const config = resourceConfig[resourceType]; | |||
| const layout = { | |||
| labelCol: { span: 24 }, | |||
| @@ -59,57 +53,57 @@ function ExportModelModal({ | |||
| }; | |||
| useEffect(() => { | |||
| requestModelList(); | |||
| requestResourceList(); | |||
| }, []); | |||
| // 获取选中的模型 | |||
| const getSelectedModel = (id: number | undefined) => { | |||
| // 获取选中的数据集、模型 | |||
| const getSelectedResource = (id: number | undefined) => { | |||
| if (id) { | |||
| return models.find((item) => item.id === id); | |||
| return resources.find((item) => item.id === id); | |||
| } | |||
| return undefined; | |||
| }; | |||
| // 模型版本 tooltip | |||
| // 版本 tooltip | |||
| const getTooltip = () => { | |||
| const id = form.getFieldValue('id'); | |||
| const model = getSelectedModel(id); | |||
| const name = model?.name ?? ''; | |||
| const resource = getSelectedResource(id); | |||
| const name = resource?.name ?? ''; | |||
| const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、'); | |||
| const tooltip = | |||
| versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined; | |||
| return tooltip; | |||
| }; | |||
| // 处理模型名称变化 | |||
| const handleModelChange = (id: number | undefined) => { | |||
| // 处理数据集、模型选择变化 | |||
| const handleResourceChange = (id: number | undefined) => { | |||
| if (id) { | |||
| getModelVersions(id); | |||
| getRecourceVersions(id); | |||
| } else { | |||
| setVersions([]); | |||
| } | |||
| }; | |||
| // 获取模型列表 | |||
| const requestModelList = async () => { | |||
| // 获取数据集、模型列表 | |||
| const requestResourceList = async () => { | |||
| const params = { | |||
| page: 0, | |||
| size: 1000, | |||
| is_public: false, // 个人 | |||
| }; | |||
| const [res] = await to(getModelList(params)); | |||
| const [res] = await to(config.getList(params)); | |||
| if (res && res.data) { | |||
| setModels(res.data.content || []); | |||
| setResources(res.data.content || []); | |||
| } | |||
| }; | |||
| // 获取模型版本列表 | |||
| const getModelVersions = async (id: number) => { | |||
| const model = getSelectedModel(id); | |||
| if (!model) { | |||
| // 获取数据集、模型版本列表 | |||
| const getRecourceVersions = async (id: number) => { | |||
| const resource = getSelectedResource(id); | |||
| if (!resource) { | |||
| return; | |||
| } | |||
| const [res] = await to(getModelVersionList(pick(model, ['identifier', 'owner']))); | |||
| const [res] = await to(config.getVersions(pick(resource, ['identifier', 'owner']))); | |||
| if (res && res.data) { | |||
| setVersions(res.data); | |||
| } | |||
| @@ -117,18 +111,18 @@ function ExportModelModal({ | |||
| // 提交 | |||
| const hanldeFinish = (formData: FormData) => { | |||
| exportToModel(formData); | |||
| exportToResource(formData); | |||
| }; | |||
| // 导出到模型 | |||
| const exportToModel = async (formData: FormData) => { | |||
| // 导出到数据集、模型 | |||
| const exportToResource = async (formData: FormData) => { | |||
| const id = form.getFieldValue('id'); | |||
| const model = getSelectedModel(id); | |||
| const resource = getSelectedResource(id); | |||
| const params = { | |||
| ...formData, | |||
| identifier: model?.identifier, | |||
| name: model?.name, | |||
| model_source: DataSource.HandExport, | |||
| identifier: resource?.identifier, | |||
| name: resource?.name, | |||
| [config.sourceParamKey]: DataSource.HandExport, | |||
| train_task: { | |||
| workflow_id: pipelineId, | |||
| experiment_id: experimentId, | |||
| @@ -136,13 +130,13 @@ function ExportModelModal({ | |||
| ins_id: experimentInsId, | |||
| task_id: pipelineNodeId, | |||
| }, | |||
| model_version_vos: [ | |||
| [config.filePropKey]: [ | |||
| { | |||
| url: path, | |||
| }, | |||
| ], | |||
| }; | |||
| const [res] = await to(addModelVersion(params)); | |||
| const [res] = await to(config.addVersion(params)); | |||
| if (res) { | |||
| onOk(); | |||
| } | |||
| @@ -151,7 +145,7 @@ function ExportModelModal({ | |||
| return ( | |||
| <KFModal | |||
| {...rest} | |||
| title="导出到模型库" | |||
| title={`导出到${config.name}`} | |||
| image={editExperimentIcon} | |||
| okButtonProps={{ | |||
| htmlType: 'submit', | |||
| @@ -170,17 +164,21 @@ function ExportModelModal({ | |||
| labelAlign="left" | |||
| labelWrap | |||
| > | |||
| <Form.Item label="模型名称" name="id" rules={[{ required: true, message: '请选择模型' }]}> | |||
| <Form.Item | |||
| label={`${config.name}名称`} | |||
| name="id" | |||
| rules={[{ required: true, message: `请选择${config.name}` }]} | |||
| > | |||
| <Select | |||
| placeholder="请选择模型" | |||
| onChange={handleModelChange} | |||
| options={models} | |||
| placeholder={`请选择${config.name}`} | |||
| onChange={handleResourceChange} | |||
| options={resources} | |||
| fieldNames={{ label: 'name', value: 'id' }} | |||
| allowClear | |||
| ></Select> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="模型版本" | |||
| label={`${config.name}版本`} | |||
| name="version" | |||
| tooltip={ | |||
| getTooltip() | |||
| @@ -192,11 +190,11 @@ function ExportModelModal({ | |||
| : undefined | |||
| } | |||
| rules={[ | |||
| { required: true, message: '请输入模型版本' }, | |||
| { required: true, message: `请输入${config.name}版本` }, | |||
| { | |||
| validator: (_, value) => { | |||
| if (value && versions.map((item) => item.name).includes(value)) { | |||
| return Promise.reject('模型版本已存在'); | |||
| return Promise.reject(`${config.name}版本已存在`); | |||
| } else { | |||
| return Promise.resolve(); | |||
| } | |||
| @@ -204,7 +202,7 @@ function ExportModelModal({ | |||
| }, | |||
| ]} | |||
| > | |||
| <Input placeholder="请输入模型版本" maxLength={64} showCount allowClear /> | |||
| <Input placeholder={`请输入${config.name}版本`} maxLength={64} showCount allowClear /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="版本描述" | |||
| @@ -0,0 +1,182 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 创建推理服务 | |||
| */ | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { CommonTabKeys, serviceTypeOptions } from '@/enums'; | |||
| import { createServiceReq, updateServiceReq } from '@/services/modelDeployment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { | |||
| getSessionStorageItem, | |||
| removeSessionStorageItem, | |||
| serviceInfoKey, | |||
| } from '@/utils/sessionStorage'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { App, Button, Col, Form, Input, Row, Select } from 'antd'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ServiceData, ServiceOperationType } from '../types'; | |||
| import styles from './index.less'; | |||
| // 表单数据 | |||
| export type FormData = { | |||
| service_name: string; // 服务名称 | |||
| service_type: string; // 服务类型 | |||
| description: string; // 描述 | |||
| }; | |||
| function CreateService() { | |||
| const navigate = useNavigate(); | |||
| const [form] = Form.useForm(); | |||
| const [operationType, setOperationType] = useState(ServiceOperationType.Create); | |||
| const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const { message } = App.useApp(); | |||
| useEffect(() => { | |||
| const res = getSessionStorageItem(serviceInfoKey, true); | |||
| if (res) { | |||
| setOperationType(res.operationType); | |||
| setServiceInfo(res); | |||
| form.setFieldsValue(pick(res, ['service_name', 'service_type', 'description'])); | |||
| } | |||
| return () => { | |||
| removeSessionStorageItem(serviceInfoKey); | |||
| }; | |||
| }, []); | |||
| // 创建、更新服务 | |||
| const createService = async (formData: FormData) => { | |||
| const request = | |||
| operationType === ServiceOperationType.Create ? createServiceReq : updateServiceReq; | |||
| const params = | |||
| operationType === ServiceOperationType.Create | |||
| ? formData | |||
| : { | |||
| id: serviceInfo?.id, | |||
| ...formData, | |||
| }; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| navigate(-1); | |||
| } | |||
| }; | |||
| // 提交 | |||
| const handleSubmit = (values: FormData) => { | |||
| createService(values); | |||
| }; | |||
| // 取消 | |||
| const cancel = () => { | |||
| navigate(-1); | |||
| }; | |||
| const disabled = operationType !== ServiceOperationType.Create; | |||
| const title = operationType === ServiceOperationType.Create ? '创建推理服务' : '更新推理服务'; | |||
| return ( | |||
| <div className={styles['model-deployment-create']}> | |||
| <PageTitle title={title}></PageTitle> | |||
| <div className={styles['model-deployment-create__content']}> | |||
| <div> | |||
| <Form | |||
| name="model-deployment-create" | |||
| labelCol={{ flex: '100px' }} | |||
| labelAlign="left" | |||
| form={form} | |||
| initialValues={{ upload_type: CommonTabKeys.Public }} | |||
| onFinish={handleSubmit} | |||
| size="large" | |||
| autoComplete="off" | |||
| > | |||
| <SubAreaTitle | |||
| title="基本信息" | |||
| image={require('@/assets/img/mirror-basic.png')} | |||
| style={{ marginBottom: '26px' }} | |||
| ></SubAreaTitle> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="服务名称" | |||
| name="service_name" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入服务名称', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input | |||
| placeholder="请输入服务名称" | |||
| disabled={disabled} | |||
| maxLength={30} | |||
| showCount | |||
| allowClear | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="服务类型" | |||
| name="service_type" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请选择服务类型', | |||
| }, | |||
| ]} | |||
| > | |||
| <Select placeholder="请选择服务类型" options={serviceTypeOptions} allowClear /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={20}> | |||
| <Form.Item | |||
| label="描 述" | |||
| name="description" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入描述', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input.TextArea | |||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||
| placeholder="请输入描述,最长128字符" | |||
| maxLength={128} | |||
| showCount | |||
| allowClear | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Form.Item wrapperCol={{ offset: 0, span: 16 }}> | |||
| <Button type="primary" htmlType="submit"> | |||
| 确定 | |||
| </Button> | |||
| <Button | |||
| type="default" | |||
| htmlType="button" | |||
| onClick={cancel} | |||
| style={{ marginLeft: '20px' }} | |||
| > | |||
| 取消 | |||
| </Button> | |||
| </Form.Item> | |||
| </Form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default CreateService; | |||
| @@ -0,0 +1,19 @@ | |||
| .create-service-version { | |||
| height: 100%; | |||
| &__content { | |||
| height: calc(100% - 60px); | |||
| margin-top: 10px; | |||
| padding: 30px 30px 10px; | |||
| overflow: auto; | |||
| color: @text-color; | |||
| font-size: @font-size-content; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__type { | |||
| color: @text-color; | |||
| font-size: @font-size-input-lg; | |||
| } | |||
| } | |||
| } | |||
| @@ -1,8 +1,9 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 创建模型部署 | |||
| * @Description: 创建服务版本 | |||
| */ | |||
| import CodeSelect from '@/components/CodeSelect'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import ResourceSelect, { | |||
| @@ -11,98 +12,147 @@ import ResourceSelect, { | |||
| type ParameterInputObject, | |||
| } from '@/components/ResourceSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { CommonTabKeys } from '@/enums'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { | |||
| createModelDeploymentReq, | |||
| restartModelDeploymentReq, | |||
| updateModelDeploymentReq, | |||
| createServiceVersionReq, | |||
| getServiceInfoReq, | |||
| updateServiceVersionReq, | |||
| } from '@/services/modelDeployment'; | |||
| import { camelCaseToUnderscore, underscoreToCamelCase } from '@/utils'; | |||
| import { changePropertyName } from '@/utils'; | |||
| import { to } from '@/utils/promise'; | |||
| import { | |||
| getSessionStorageItem, | |||
| modelDeploymentInfoKey, | |||
| removeSessionStorageItem, | |||
| serviceVersionInfoKey, | |||
| } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { PlusOutlined } from '@ant-design/icons'; | |||
| import { useNavigate, useParams } from '@umijs/max'; | |||
| import { App, Button, Col, Flex, Form, Input, Row, Select } from 'antd'; | |||
| import { omit, pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import { ModelDeploymentData, ModelDeploymentOperationType } from '../types'; | |||
| import { ServiceData, ServiceOperationType, ServiceVersionData } from '../types'; | |||
| import styles from './index.less'; | |||
| // 表单数据 | |||
| export type FormData = { | |||
| serviceName: string; // 服务名称 | |||
| service_name: string; // 服务名称 | |||
| version: string; // 服务版本 | |||
| description: string; // 描述 | |||
| model: ParameterInputObject; // 模型 | |||
| image: ParameterInputObject; // 镜像 | |||
| code_config: ParameterInputObject; // 代码 | |||
| resource: string; // 资源规格 | |||
| replicas: string; // 副本数量 | |||
| modelPath: string; // 模型路径 | |||
| env: { key: string; value: string }[]; // 环境变量 | |||
| mount_path: string; // 模型路径 | |||
| env_variables: { key: string; value: string }[]; // 环境变量 | |||
| }; | |||
| function ModelDeploymentCreate() { | |||
| function CreateServiceVersion() { | |||
| const navigate = useNavigate(); | |||
| const [form] = Form.useForm(); | |||
| const [resourceStandardList, filterResourceStandard] = useComputingResource(); | |||
| const [operationType, setOperationType] = useState(ModelDeploymentOperationType.Create); | |||
| const [modelDeploymentInfo, setModelDeploymentInfo] = useState<ModelDeploymentData | undefined>( | |||
| undefined, | |||
| ); | |||
| const [operationType, setOperationType] = useState(ServiceOperationType.Create); | |||
| const { message } = App.useApp(); | |||
| const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined); | |||
| const params = useParams(); | |||
| const id = params.id; | |||
| useEffect(() => { | |||
| const res = getSessionStorageItem(modelDeploymentInfoKey, true); | |||
| const res: (ServiceVersionData & { operationType: ServiceOperationType }) | undefined = | |||
| getSessionStorageItem(serviceVersionInfoKey, true); | |||
| if (res) { | |||
| setOperationType(res.operationType); | |||
| setModelDeploymentInfo(res); | |||
| const formData = underscoreToCamelCase(res) as FormData; | |||
| setVersionInfo(res); | |||
| let model, codeConfig, envVariables; | |||
| if (res.model && typeof res.model === 'object') { | |||
| model = changePropertyName(res.model, { show_value: 'showValue' }); | |||
| // 接口返回是数据没有 value 值,但是 form 需要 value | |||
| model.value = model.showValue; | |||
| } | |||
| if (res.code_config && typeof res.code_config === 'object') { | |||
| codeConfig = changePropertyName(res.code_config, { show_value: 'showValue' }); | |||
| // 接口返回是数据没有 value 值,但是 form 需要 value | |||
| codeConfig.value = codeConfig.showValue; | |||
| } | |||
| if (res.env_variables && typeof res.env_variables === 'object') { | |||
| envVariables = Object.entries(res.env_variables).map(([key, value]) => ({ | |||
| key, | |||
| value, | |||
| })); | |||
| } | |||
| const formData = { | |||
| ...omit(res, 'model', 'code_config', 'env_variables'), | |||
| model: model, | |||
| code_config: codeConfig, | |||
| env_variables: envVariables, | |||
| }; | |||
| form.setFieldsValue(formData); | |||
| } | |||
| return () => { | |||
| removeSessionStorageItem(modelDeploymentInfoKey); | |||
| removeSessionStorageItem(serviceVersionInfoKey); | |||
| }; | |||
| }, []); | |||
| // 创建 | |||
| const createModelDeployment = async (formData: FormData) => { | |||
| const envList = formData['env'] ?? []; | |||
| useEffect(() => { | |||
| getServiceInfo(); | |||
| }, []); | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(id)); | |||
| if (res && res.data) { | |||
| setServiceInfo(res.data); | |||
| form.setFieldsValue({ | |||
| service_name: res.data.service_name, | |||
| }); | |||
| } | |||
| }; | |||
| // 创建版本 | |||
| const createServiceVersion = async (formData: FormData) => { | |||
| const envList = formData['env_variables'] ?? []; | |||
| const image = formData['image']; | |||
| const model = formData['model']; | |||
| const env = envList.reduce((acc, cur) => { | |||
| const codeConfig = formData['code_config']; | |||
| const envVariables = envList.reduce((acc, cur) => { | |||
| acc[cur.key] = cur.value; | |||
| return acc; | |||
| }, {} as Record<string, string>); | |||
| // 根据后台要求,修改表单数据 | |||
| const object = camelCaseToUnderscore({ | |||
| ...omit(formData, ['replicas', 'env', 'image', 'model']), | |||
| const object = { | |||
| ...omit(formData, ['replicas', 'env_variables', 'image', 'model', 'code_config']), | |||
| replicas: Number(formData.replicas), | |||
| env, | |||
| env_variables: envVariables, | |||
| image: image.value, | |||
| model: pick(model, ['id', 'version', 'path', 'showValue']), | |||
| }); | |||
| model: changePropertyName( | |||
| pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']), | |||
| { showValue: 'show_value' }, | |||
| ), | |||
| code_config: changePropertyName(pick(codeConfig, ['code_path', 'branch', 'showValue']), { | |||
| showValue: 'show_value', | |||
| }), | |||
| service_id: serviceInfo?.id, | |||
| }; | |||
| const params = | |||
| operationType === ModelDeploymentOperationType.Create | |||
| operationType === ServiceOperationType.Create | |||
| ? object | |||
| : { | |||
| ...pick(modelDeploymentInfo, ['service_id', 'service_ins_id']), | |||
| update_model: { | |||
| ...pick(object, ['description', 'env', 'replicas', 'resource', 'image']), | |||
| }, | |||
| id: versionInfo?.id, | |||
| rerun: operationType === ServiceOperationType.Restart ? true : false, | |||
| deployment_name: versionInfo?.deployment_name, | |||
| ...object, | |||
| }; | |||
| let request = createModelDeploymentReq; | |||
| if (operationType === ModelDeploymentOperationType.Restart) { | |||
| request = restartModelDeploymentReq; | |||
| } else if (operationType === ModelDeploymentOperationType.Update) { | |||
| request = updateModelDeploymentReq; | |||
| } | |||
| const request = | |||
| operationType === ServiceOperationType.Create | |||
| ? createServiceVersionReq | |||
| : updateServiceVersionReq; | |||
| const [res] = await to(request(params)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| @@ -112,7 +162,7 @@ function ModelDeploymentCreate() { | |||
| // 提交 | |||
| const handleSubmit = (values: FormData) => { | |||
| createModelDeployment(values); | |||
| createServiceVersion(values); | |||
| }; | |||
| // 取消 | |||
| @@ -120,25 +170,27 @@ function ModelDeploymentCreate() { | |||
| navigate(-1); | |||
| }; | |||
| const disabled = operationType !== ModelDeploymentOperationType.Create; | |||
| const disabled = operationType !== ServiceOperationType.Create; | |||
| let buttonText = '新建'; | |||
| if (operationType === ModelDeploymentOperationType.Update) { | |||
| let title = '新增服务版本'; | |||
| if (operationType === ServiceOperationType.Update) { | |||
| title = '更新服务版本'; | |||
| buttonText = '更新'; | |||
| } else if (operationType === ModelDeploymentOperationType.Restart) { | |||
| } else if (operationType === ServiceOperationType.Restart) { | |||
| title = '重启服务版本'; | |||
| buttonText = '重启'; | |||
| } | |||
| return ( | |||
| <div className={styles['model-deployment-create']}> | |||
| <PageTitle title="创建推理服务"></PageTitle> | |||
| <div className={styles['model-deployment-create__content']}> | |||
| <div className={styles['create-service-version']}> | |||
| <PageTitle title={title}></PageTitle> | |||
| <div className={styles['create-service-version__content']}> | |||
| <div> | |||
| <Form | |||
| name="model-deployment-create" | |||
| name="create-service-version" | |||
| labelCol={{ flex: '100px' }} | |||
| labelAlign="left" | |||
| form={form} | |||
| initialValues={{ upload_type: CommonTabKeys.Public }} | |||
| onFinish={handleSubmit} | |||
| size="large" | |||
| autoComplete="off" | |||
| @@ -152,7 +204,7 @@ function ModelDeploymentCreate() { | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="服务名称" | |||
| name="serviceName" | |||
| name="service_name" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| @@ -162,8 +214,34 @@ function ModelDeploymentCreate() { | |||
| > | |||
| <Input | |||
| placeholder="请输入服务名称" | |||
| disabled={disabled} | |||
| maxLength={30} | |||
| disabled | |||
| showCount | |||
| allowClear | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="服务版本" | |||
| name="version" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入服务版本', | |||
| }, | |||
| { | |||
| pattern: /^[a-zA-Z0-9._-]+$/, | |||
| message: '版本只支持字母、数字、下划线、点、横杠', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input | |||
| placeholder="请输入服务版本" | |||
| maxLength={30} | |||
| disabled={disabled} | |||
| showCount | |||
| allowClear | |||
| /> | |||
| @@ -173,18 +251,18 @@ function ModelDeploymentCreate() { | |||
| <Row gutter={8}> | |||
| <Col span={20}> | |||
| <Form.Item | |||
| label="描 述" | |||
| label="版本描述" | |||
| name="description" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入描述', | |||
| message: '请输入版本描述', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input.TextArea | |||
| autoSize={{ minRows: 2, maxRows: 6 }} | |||
| placeholder="请输入描述,最长128字符" | |||
| placeholder="请输入版本描述,最长128字符" | |||
| maxLength={128} | |||
| showCount | |||
| allowClear | |||
| @@ -238,6 +316,29 @@ function ModelDeploymentCreate() { | |||
| placeholder="请选择镜像" | |||
| canInput={false} | |||
| size="large" | |||
| disabled={disabled} | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="代码配置" | |||
| name="code_config" | |||
| rules={[ | |||
| { | |||
| validator: requiredValidator, | |||
| message: '请选择代码配置', | |||
| }, | |||
| ]} | |||
| required | |||
| > | |||
| <CodeSelect | |||
| placeholder="请选择代码配置" | |||
| canInput={false} | |||
| size="large" | |||
| disabled={disabled} | |||
| /> | |||
| </Form.Item> | |||
| </Col> | |||
| @@ -292,12 +393,16 @@ function ModelDeploymentCreate() { | |||
| <Col span={10}> | |||
| <Form.Item | |||
| label="挂载路径" | |||
| name="modelPath" | |||
| name="mount_path" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入模型挂载路径', | |||
| }, | |||
| { | |||
| pattern: /^\/[a-zA-Z0-9._/-]+$/, | |||
| message: '请输入正确的挂载绝对路径', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input | |||
| @@ -311,15 +416,22 @@ function ModelDeploymentCreate() { | |||
| </Col> | |||
| </Row> | |||
| <Form.List name="env"> | |||
| <Form.List name="env_variables"> | |||
| {(fields, { add, remove }) => ( | |||
| <> | |||
| <Row gutter={8}> | |||
| <Col span={10}> | |||
| <Form.Item label="环境变量"> | |||
| <Button type="link" style={{ padding: '0' }} onClick={() => add()}> | |||
| 添加环境变量 | |||
| </Button> | |||
| {fields.length === 0 ? ( | |||
| <Button | |||
| type="link" | |||
| style={{ padding: '0' }} | |||
| onClick={() => add()} | |||
| disabled={disabled} | |||
| > | |||
| 添加环境变量 | |||
| </Button> | |||
| ) : null} | |||
| </Form.Item> | |||
| </Col> | |||
| </Row> | |||
| @@ -329,9 +441,16 @@ function ModelDeploymentCreate() { | |||
| {...restField} | |||
| name={[name, 'key']} | |||
| style={{ flex: 1 }} | |||
| rules={[{ required: true, message: '请输入变量名' }]} | |||
| rules={[ | |||
| { required: true, message: '请输入变量名' }, | |||
| { | |||
| pattern: /^[a-zA-Z_][a-zA-Z0-9_-]*$/, | |||
| message: | |||
| '变量名只支持字母、数字、下划线、中横线且开头必须是字母或下划线', | |||
| }, | |||
| ]} | |||
| > | |||
| <Input placeholder="请输入变量名" /> | |||
| <Input placeholder="请输入变量名" disabled={disabled} /> | |||
| </Form.Item> | |||
| <span style={{ marginBottom: '24px' }}>=</span> | |||
| <Form.Item | |||
| @@ -340,15 +459,17 @@ function ModelDeploymentCreate() { | |||
| style={{ flex: 1 }} | |||
| rules={[{ required: true, message: '请输入变量值' }]} | |||
| > | |||
| <Input placeholder="请输入变量值" /> | |||
| <Input placeholder="请输入变量值" disabled={disabled} /> | |||
| </Form.Item> | |||
| <Button | |||
| type="link" | |||
| style={{ marginBottom: '24px' }} | |||
| icon={<KFIcon type="icon-shanchu" font={16} />} | |||
| disabled={disabled} | |||
| onClick={() => { | |||
| modalConfirm({ | |||
| content: '是否确认删除?', | |||
| title: '删除', | |||
| content: '是否确认要删除该环境变量?', | |||
| onOk: () => { | |||
| remove(name); | |||
| }, | |||
| @@ -357,10 +478,20 @@ function ModelDeploymentCreate() { | |||
| ></Button> | |||
| </Flex> | |||
| ))} | |||
| {fields.length > 0 ? ( | |||
| <Button | |||
| type="link" | |||
| style={{ padding: '0', margin: '-24px 0 24px' }} | |||
| onClick={() => add()} | |||
| icon={<PlusOutlined />} | |||
| disabled={disabled} | |||
| > | |||
| 环境变量 | |||
| </Button> | |||
| ) : null} | |||
| </> | |||
| )} | |||
| </Form.List> | |||
| <Form.Item wrapperCol={{ offset: 0, span: 16 }}> | |||
| <Button type="primary" htmlType="submit"> | |||
| {buttonText} | |||
| @@ -381,4 +512,4 @@ function ModelDeploymentCreate() { | |||
| ); | |||
| } | |||
| export default ModelDeploymentCreate; | |||
| export default CreateServiceVersion; | |||
| @@ -1,22 +1,18 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 模型部署列表 | |||
| * @Description: 模型部署服务列表 | |||
| */ | |||
| import CommonTableCell from '@/components/CommonTableCell'; | |||
| import DateTableCell from '@/components/DateTableCell'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import { ModelDeploymentStatus, modelDeploymentStatusOptions } from '@/enums'; | |||
| import { serviceTypeOptions } from '@/enums'; | |||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||
| import { | |||
| deleteModelDeploymentReq, | |||
| getModelDeploymentListReq, | |||
| stopModelDeploymentReq, | |||
| } from '@/services/modelDeployment'; | |||
| import { deleteServiceReq, getServiceListReq } from '@/services/modelDeployment'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { to } from '@/utils/promise'; | |||
| import { modelDeploymentInfoKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { serviceInfoKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate } from '@umijs/max'; | |||
| import { | |||
| @@ -31,20 +27,20 @@ import { | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import ModelDeploymentStatusCell from '../components/ModelDeployStatusCell'; | |||
| import { ModelDeploymentData, ModelDeploymentOperationType } from '../types'; | |||
| import { ServiceData, ServiceOperationType } from '../types'; | |||
| import styles from './index.less'; | |||
| const allServiceTypeOptions = [{ label: '全部', value: '' }, ...serviceTypeOptions]; | |||
| function ModelDeployment() { | |||
| const navigate = useNavigate(); | |||
| const { message } = App.useApp(); | |||
| const [cacheState, setCacheState] = useCacheState(); | |||
| const [searchStatus, setSearchStatus] = useState(cacheState?.searchStatus ?? ''); | |||
| const [serviceType, setServiceType] = useState(cacheState?.serviceType ?? ''); | |||
| const [searchText, setSearchText] = useState(cacheState?.searchText); | |||
| const [inputText, setInputText] = useState(cacheState?.searchText); | |||
| const [tableData, setTableData] = useState<ModelDeploymentData[]>([]); | |||
| const [tableData, setTableData] = useState<ServiceData[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | |||
| cacheState?.pagination ?? { | |||
| @@ -54,29 +50,28 @@ function ModelDeployment() { | |||
| ); | |||
| useEffect(() => { | |||
| getModelDeploymentList(); | |||
| }, [pagination, searchText, searchStatus]); | |||
| getServiceList(); | |||
| }, [pagination, searchText, serviceType]); | |||
| // 获取模型部署列表 | |||
| const getModelDeploymentList = async () => { | |||
| // 获取模型部署服务列表 | |||
| const getServiceList = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current!, | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| service_name: searchText, | |||
| status: searchStatus, | |||
| service_type: serviceType, | |||
| }; | |||
| const [res] = await to(getModelDeploymentListReq(params)); | |||
| const [res] = await to(getServiceListReq(params)); | |||
| if (res && res.data) { | |||
| const { service_list = [], total = 0 } = res.data; | |||
| setTableData(service_list); | |||
| setTotal(total); | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| // 删除模型部署 | |||
| const deleteModelDeploy = async (record: ModelDeploymentData) => { | |||
| const params = pick(record, ['service_id', 'service_ins_id']); | |||
| const [res] = await to(deleteModelDeploymentReq(params)); | |||
| const deleteService = async (record: ServiceData) => { | |||
| const [res] = await to(deleteServiceReq(record.id)); | |||
| if (res) { | |||
| message.success('删除成功'); | |||
| // 如果是一页的唯一数据,删除时,请求第一页的数据 | |||
| @@ -88,54 +83,31 @@ function ModelDeployment() { | |||
| current: 1, | |||
| })); | |||
| } else { | |||
| getModelDeploymentList(); | |||
| getServiceList(); | |||
| } | |||
| } | |||
| }; | |||
| // 停止模型部署 | |||
| const stopModelDeploy = async (record: ModelDeploymentData) => { | |||
| const params = pick(record, ['service_id', 'service_ins_id']); | |||
| const [res] = await to(stopModelDeploymentReq(params)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| getModelDeploymentList(); | |||
| } | |||
| }; | |||
| // 搜索 | |||
| const onSearch: SearchProps['onSearch'] = (value) => { | |||
| setSearchText(value); | |||
| }; | |||
| // 处理删除 | |||
| const handleModelDeployDelete = (record: ModelDeploymentData) => { | |||
| const handleServiceDelete = (record: ServiceData) => { | |||
| modalConfirm({ | |||
| title: '删除后,该模型部署将不可恢复', | |||
| title: '删除后,该服务将不可恢复', | |||
| content: '是否确认删除?', | |||
| onOk: () => { | |||
| deleteModelDeploy(record); | |||
| deleteService(record); | |||
| }, | |||
| }); | |||
| }; | |||
| // 处理停止 | |||
| const handleModelDeployStop = async (record: ModelDeploymentData) => { | |||
| modalConfirm({ | |||
| content: '是否确认停止?', | |||
| onOk: () => { | |||
| stopModelDeploy(record); | |||
| }, | |||
| }); | |||
| }; | |||
| // 创建、更新、重启模型部署 | |||
| const createModelDeployment = ( | |||
| type: ModelDeploymentOperationType, | |||
| record?: ModelDeploymentData, | |||
| ) => { | |||
| // 创建、更新服务 | |||
| const createService = (type: ServiceOperationType, record?: ServiceData) => { | |||
| setSessionStorageItem( | |||
| modelDeploymentInfoKey, | |||
| serviceInfoKey, | |||
| { | |||
| ...record, | |||
| operationType: type, | |||
| @@ -146,23 +118,23 @@ function ModelDeployment() { | |||
| setCacheState({ | |||
| pagination, | |||
| searchText, | |||
| searchStatus, | |||
| serviceType: serviceType, | |||
| }); | |||
| navigate(`/modelDeployment/create`); | |||
| navigate(`/modelDeployment/createService`); | |||
| }; | |||
| // 查看详情 | |||
| const toDetail = (record: ModelDeploymentData) => { | |||
| setSessionStorageItem(modelDeploymentInfoKey, record, true); | |||
| const toDetail = (record: ServiceData) => { | |||
| setSessionStorageItem(serviceInfoKey, record, true); | |||
| setCacheState({ | |||
| pagination, | |||
| searchText, | |||
| searchStatus, | |||
| serviceType: serviceType, | |||
| }); | |||
| navigate(`/modelDeployment/info/${record.service_id}`); | |||
| navigate(`/modelDeployment/serviceInfo/${record.id}`); | |||
| }; | |||
| // 分页切换 | |||
| @@ -173,7 +145,7 @@ function ModelDeployment() { | |||
| // console.log(pagination, filters, sorter, action); | |||
| }; | |||
| const columns: TableProps<ModelDeploymentData>['columns'] = [ | |||
| const columns: TableProps<ServiceData>['columns'] = [ | |||
| { | |||
| title: '序号', | |||
| dataIndex: 'index', | |||
| @@ -197,23 +169,23 @@ function ModelDeployment() { | |||
| }, | |||
| }, | |||
| { | |||
| title: '模型', | |||
| dataIndex: ['model', 'show_value'], | |||
| key: 'model', | |||
| title: '服务类型', | |||
| dataIndex: 'service_type_name', | |||
| key: 'service_type_name', | |||
| width: '20%', | |||
| render: CommonTableCell(), | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'status', | |||
| key: 'status', | |||
| title: '版本数量', | |||
| dataIndex: 'version_count', | |||
| key: 'version_count', | |||
| width: '20%', | |||
| render: ModelDeploymentStatusCell, | |||
| render: CommonTableCell(), | |||
| }, | |||
| { | |||
| title: '创建人', | |||
| dataIndex: 'created_by', | |||
| key: 'created_by', | |||
| title: '服务描述', | |||
| dataIndex: 'description', | |||
| key: 'description', | |||
| render: CommonTableCell(), | |||
| width: '20%', | |||
| }, | |||
| @@ -227,44 +199,28 @@ function ModelDeployment() { | |||
| { | |||
| title: '操作', | |||
| dataIndex: 'operation', | |||
| width: 250, | |||
| width: 300, | |||
| key: 'operation', | |||
| render: (_: any, record: ModelDeploymentData) => ( | |||
| render: (_: any, record: ServiceData) => ( | |||
| <div> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="edit" | |||
| icon={<KFIcon type="icon-bianji" />} | |||
| onClick={() => createModelDeployment(ModelDeploymentOperationType.Update, record)} | |||
| onClick={() => createService(ServiceOperationType.Update, record)} | |||
| > | |||
| 更新 | |||
| 编辑 | |||
| </Button> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="run" | |||
| icon={<KFIcon type="icon-xiangqing" />} | |||
| onClick={() => toDetail(record)} | |||
| > | |||
| 查看详情 | |||
| </Button> | |||
| {(record.status === ModelDeploymentStatus.Failed || | |||
| record.status === ModelDeploymentStatus.Stopped) && ( | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="run" | |||
| icon={<KFIcon type="icon-yunhang" />} | |||
| onClick={() => createModelDeployment(ModelDeploymentOperationType.Restart, record)} | |||
| > | |||
| 重启 | |||
| </Button> | |||
| )} | |||
| {(record.status === ModelDeploymentStatus.Running || | |||
| record.status === ModelDeploymentStatus.Init || | |||
| record.status === ModelDeploymentStatus.Pending) && ( | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="stop" | |||
| icon={<KFIcon type="icon-tingzhi" />} | |||
| onClick={() => handleModelDeployStop(record)} | |||
| > | |||
| 停止 | |||
| </Button> | |||
| )} | |||
| <ConfigProvider | |||
| theme={{ | |||
| token: { | |||
| @@ -277,7 +233,7 @@ function ModelDeployment() { | |||
| size="small" | |||
| key="remove" | |||
| icon={<KFIcon type="icon-shanchu" />} | |||
| onClick={() => handleModelDeployDelete(record)} | |||
| onClick={() => handleServiceDelete(record)} | |||
| > | |||
| 删除 | |||
| </Button> | |||
| @@ -289,11 +245,11 @@ function ModelDeployment() { | |||
| return ( | |||
| <div className={styles['model-deployment']}> | |||
| <PageTitle title="模型列表"></PageTitle> | |||
| <PageTitle title="服务列表"></PageTitle> | |||
| <div className={styles['model-deployment__content']}> | |||
| <div className={styles['model-deployment__content__filter']}> | |||
| <Input.Search | |||
| placeholder="按模型服务名称筛选" | |||
| placeholder="按服务名称筛选" | |||
| onSearch={onSearch} | |||
| onChange={(e) => setInputText(e.target.value)} | |||
| style={{ width: 300 }} | |||
| @@ -303,27 +259,19 @@ function ModelDeployment() { | |||
| <Select | |||
| style={{ width: 100, marginLeft: '20px' }} | |||
| placeholder="请选择" | |||
| onChange={(value) => setSearchStatus(value)} | |||
| options={modelDeploymentStatusOptions} | |||
| value={searchStatus} | |||
| onChange={(value) => setServiceType(value ?? '')} | |||
| options={allServiceTypeOptions} | |||
| value={serviceType} | |||
| allowClear | |||
| ></Select> | |||
| <Button | |||
| style={{ marginLeft: '20px' }} | |||
| style={{ marginLeft: 'auto', marginRight: '20px' }} | |||
| type="default" | |||
| onClick={() => createModelDeployment(ModelDeploymentOperationType.Create)} | |||
| onClick={() => createService(ServiceOperationType.Create)} | |||
| icon={<KFIcon type="icon-xinjian2" />} | |||
| > | |||
| 创建推理服务 | |||
| </Button> | |||
| <Button | |||
| style={{ marginRight: 0, marginLeft: 'auto' }} | |||
| type="default" | |||
| onClick={getModelDeploymentList} | |||
| icon={<KFIcon type="icon-shuaxin" />} | |||
| > | |||
| 刷新 | |||
| </Button> | |||
| </div> | |||
| <div | |||
| className={classNames( | |||
| @@ -343,7 +291,7 @@ function ModelDeployment() { | |||
| showTotal: () => `共${total}条`, | |||
| }} | |||
| onChange={handleTableChange} | |||
| rowKey="service_id" | |||
| rowKey="id" | |||
| /> | |||
| </div> | |||
| </div> | |||
| @@ -0,0 +1,24 @@ | |||
| .service-info { | |||
| height: 100%; | |||
| &__content { | |||
| display: flex; | |||
| flex-direction: column; | |||
| height: calc(100% - 60px); | |||
| margin-top: 10px; | |||
| padding: 20px 30px 0; | |||
| background-color: white; | |||
| border-radius: 10px; | |||
| &__filter { | |||
| display: flex; | |||
| flex: none; | |||
| align-items: center; | |||
| justify-content: space-between; | |||
| } | |||
| &__table { | |||
| flex: 1; | |||
| margin-top: 24px; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,431 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 模型部署列表 | |||
| */ | |||
| import BasicInfo from '@/components/BasicInfo'; | |||
| import CommonTableCell from '@/components/CommonTableCell'; | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; | |||
| import { useCacheState } from '@/hooks/pageCacheState'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { | |||
| deleteServiceVersionReq, | |||
| getServiceInfoReq, | |||
| getServiceVersionsReq, | |||
| stopServiceVersionReq, | |||
| } from '@/services/modelDeployment'; | |||
| import themes from '@/styles/theme.less'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { to } from '@/utils/promise'; | |||
| import { serviceVersionInfoKey, setSessionStorageItem } from '@/utils/sessionStorage'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { useNavigate, useParams } from '@umijs/max'; | |||
| import { | |||
| App, | |||
| Button, | |||
| ConfigProvider, | |||
| Input, | |||
| Select, | |||
| Table, | |||
| Tooltip, | |||
| type TablePaginationConfig, | |||
| type TableProps, | |||
| } from 'antd'; | |||
| import { type SearchProps } from 'antd/es/input'; | |||
| import classNames from 'classnames'; | |||
| import { useEffect, useState } from 'react'; | |||
| import ServiceRunStatusCell from '../components/ModelDeployStatusCell'; | |||
| import { ServiceData, ServiceOperationType, ServiceVersionData } from '../types'; | |||
| import styles from './index.less'; | |||
| const allServiceStatusOptions = [{ label: '全部', value: '' }, ...serviceStatusOptions]; | |||
| function ServiceInfo() { | |||
| const navigate = useNavigate(); | |||
| const { message } = App.useApp(); | |||
| const [cacheState, setCacheState] = useCacheState(); | |||
| const [serviceStatus, setServiceStatus] = useState(cacheState?.serviceStatus ?? ''); | |||
| const [searchText, setSearchText] = useState(cacheState?.searchText); | |||
| const [inputText, setInputText] = useState(cacheState?.searchText); | |||
| const [tableData, setTableData] = useState<ServiceVersionData[]>([]); | |||
| const [total, setTotal] = useState(0); | |||
| const [pagination, setPagination] = useState<TablePaginationConfig>( | |||
| cacheState?.pagination ?? { | |||
| current: 1, | |||
| pageSize: 10, | |||
| }, | |||
| ); | |||
| const params = useParams(); | |||
| const id = params.id; | |||
| const [serviceInfo, setServiceInfo] = useState<ServiceData | undefined>(undefined); | |||
| const basicInfo = [ | |||
| { | |||
| label: '服务名称', | |||
| value: serviceInfo?.service_name, | |||
| }, | |||
| { | |||
| label: '服务描述', | |||
| value: serviceInfo?.description, | |||
| }, | |||
| { | |||
| label: '版本数量', | |||
| value: serviceInfo?.version_count, | |||
| }, | |||
| { | |||
| label: '创建时间', | |||
| value: serviceInfo?.create_time, | |||
| format: formatDate, | |||
| }, | |||
| ]; | |||
| const getResourceDescription = useComputingResource()[2]; | |||
| useEffect(() => { | |||
| getServiceInfo(); | |||
| }, []); | |||
| useEffect(() => { | |||
| getServiceVersions(); | |||
| }, [pagination, searchText, serviceStatus]); | |||
| // 获取服务详情 | |||
| const getServiceInfo = async () => { | |||
| const [res] = await to(getServiceInfoReq(id)); | |||
| if (res && res.data) { | |||
| setServiceInfo(res.data); | |||
| } | |||
| }; | |||
| // 获取服务版本列表 | |||
| const getServiceVersions = async () => { | |||
| const params: Record<string, any> = { | |||
| page: pagination.current! - 1, | |||
| size: pagination.pageSize, | |||
| version: searchText, | |||
| run_state: serviceStatus, | |||
| service_id: id, | |||
| }; | |||
| const [res] = await to(getServiceVersionsReq(params)); | |||
| if (res && res.data) { | |||
| const { content = [], totalElements = 0 } = res.data; | |||
| setTableData(content); | |||
| setTotal(totalElements); | |||
| } | |||
| }; | |||
| // 删除模型部署 | |||
| const deleteServiceVersion = async (record: ServiceVersionData) => { | |||
| const [res] = await to(deleteServiceVersionReq(record.id)); | |||
| if (res) { | |||
| message.success('删除成功'); | |||
| // 如果是一页的唯一数据,删除时,请求第一页的数据 | |||
| // 否则直接刷新这一页的数据 | |||
| // 避免回到第一页 | |||
| if (tableData.length > 1) { | |||
| setPagination((prev) => ({ | |||
| ...prev, | |||
| current: 1, | |||
| })); | |||
| } else { | |||
| getServiceInfo(); | |||
| getServiceVersions(); | |||
| } | |||
| } | |||
| }; | |||
| // 停止模型部署 | |||
| const stopServiceVersion = async (record: ServiceVersionData) => { | |||
| const [res] = await to(stopServiceVersionReq(record.id)); | |||
| if (res) { | |||
| message.success('操作成功'); | |||
| getServiceVersions(); | |||
| } | |||
| }; | |||
| // 搜索 | |||
| const onSearch: SearchProps['onSearch'] = (value) => { | |||
| setSearchText(value); | |||
| }; | |||
| // 处理删除 | |||
| const handleServiceVersionDelete = (record: ServiceVersionData) => { | |||
| modalConfirm({ | |||
| title: '删除后,该服务版本将不可恢复', | |||
| content: '是否确认删除?', | |||
| onOk: () => { | |||
| deleteServiceVersion(record); | |||
| }, | |||
| }); | |||
| }; | |||
| // 处理停止 | |||
| const handleServiceVersionStop = async (record: ServiceVersionData) => { | |||
| modalConfirm({ | |||
| title: '停止', | |||
| content: '是否确认停止该服务?', | |||
| onOk: () => { | |||
| stopServiceVersion(record); | |||
| }, | |||
| }); | |||
| }; | |||
| // 创建、更新、重启模型部署 | |||
| const createServiceVersion = (type: ServiceOperationType, record?: ServiceVersionData) => { | |||
| setSessionStorageItem( | |||
| serviceVersionInfoKey, | |||
| { | |||
| ...record, | |||
| operationType: type, | |||
| }, | |||
| true, | |||
| ); | |||
| setCacheState({ | |||
| pagination, | |||
| searchText, | |||
| serviceStatus: serviceStatus, | |||
| }); | |||
| navigate(`/modelDeployment/addVersion/${id}`); | |||
| }; | |||
| // 查看详情 | |||
| const toDetail = (record: ServiceVersionData) => { | |||
| setSessionStorageItem(serviceVersionInfoKey, record, true); | |||
| setCacheState({ | |||
| pagination, | |||
| searchText, | |||
| serviceStatus: serviceStatus, | |||
| }); | |||
| navigate(`/modelDeployment/versionInfo/${record.id}`); | |||
| }; | |||
| // 分页切换 | |||
| const handleTableChange: TableProps['onChange'] = (pagination, _filters, _sorter, { action }) => { | |||
| if (action === 'paginate') { | |||
| setPagination(pagination); | |||
| } | |||
| // console.log(pagination, filters, sorter, action); | |||
| }; | |||
| const columns: TableProps<ServiceVersionData>['columns'] = [ | |||
| { | |||
| title: '序号', | |||
| dataIndex: 'index', | |||
| key: 'index', | |||
| width: '20%', | |||
| render(_text, _record, index) { | |||
| return <span>{(pagination.current! - 1) * pagination.pageSize! + index + 1}</span>; | |||
| }, | |||
| }, | |||
| { | |||
| title: '服务版本', | |||
| dataIndex: 'version', | |||
| key: 'version', | |||
| width: '20%', | |||
| render: CommonTableCell(), | |||
| }, | |||
| { | |||
| title: '模型版本', | |||
| dataIndex: 'model', | |||
| key: 'model', | |||
| width: '20%', | |||
| render: (_text: string, record: ServiceVersionData) => ( | |||
| <Tooltip | |||
| title={record.model.show_value} | |||
| placement="topLeft" | |||
| overlayStyle={{ maxWidth: '400px' }} | |||
| > | |||
| <span>{record.model.show_value}</span> | |||
| </Tooltip> | |||
| ), | |||
| ellipsis: { showTitle: false }, | |||
| }, | |||
| { | |||
| title: '状态', | |||
| dataIndex: 'run_state', | |||
| key: 'run_state', | |||
| width: '20%', | |||
| render: ServiceRunStatusCell, | |||
| }, | |||
| { | |||
| title: '版本镜像', | |||
| dataIndex: 'image', | |||
| key: 'image', | |||
| width: '20%', | |||
| render: CommonTableCell(true), | |||
| ellipsis: { showTitle: false }, | |||
| }, | |||
| { | |||
| title: '副本数量', | |||
| dataIndex: 'replicas', | |||
| key: 'replicas', | |||
| render: CommonTableCell(), | |||
| width: '20%', | |||
| }, | |||
| { | |||
| title: '资源规格', | |||
| dataIndex: 'resource', | |||
| key: 'resource', | |||
| width: '20%', | |||
| render: (resource: string) => ( | |||
| <Tooltip | |||
| title={getResourceDescription(resource)} | |||
| placement="topLeft" | |||
| overlayStyle={{ maxWidth: '400px' }} | |||
| > | |||
| <span>{resource ? getResourceDescription(resource) : '--'}</span> | |||
| </Tooltip> | |||
| ), | |||
| ellipsis: { showTitle: false }, | |||
| }, | |||
| { | |||
| title: '操作', | |||
| dataIndex: 'operation', | |||
| width: 320, | |||
| key: 'operation', | |||
| render: (_: any, record: ServiceVersionData) => ( | |||
| <div> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="info" | |||
| icon={<KFIcon type="icon-xiangqing" />} | |||
| onClick={() => toDetail(record)} | |||
| > | |||
| 详情 | |||
| </Button> | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="edit" | |||
| icon={<KFIcon type="icon-bianji" />} | |||
| onClick={() => createServiceVersion(ServiceOperationType.Update, record)} | |||
| > | |||
| 更新 | |||
| </Button> | |||
| {(record.run_state === ServiceRunStatus.Failed || | |||
| record.run_state === ServiceRunStatus.Stopped) && ( | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="run" | |||
| icon={<KFIcon type="icon-yunhang" />} | |||
| onClick={() => createServiceVersion(ServiceOperationType.Restart, record)} | |||
| > | |||
| 重启 | |||
| </Button> | |||
| )} | |||
| {(record.run_state === ServiceRunStatus.Running || | |||
| record.run_state === ServiceRunStatus.Init || | |||
| record.run_state === ServiceRunStatus.Pending) && ( | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="stop" | |||
| icon={<KFIcon type="icon-tingzhi" />} | |||
| onClick={() => handleServiceVersionStop(record)} | |||
| > | |||
| 停止 | |||
| </Button> | |||
| )} | |||
| <ConfigProvider | |||
| theme={{ | |||
| token: { | |||
| colorLink: themes['warningColor'], | |||
| }, | |||
| }} | |||
| > | |||
| <Button | |||
| type="link" | |||
| size="small" | |||
| key="remove" | |||
| icon={<KFIcon type="icon-shanchu" />} | |||
| onClick={() => handleServiceVersionDelete(record)} | |||
| > | |||
| 删除 | |||
| </Button> | |||
| </ConfigProvider> | |||
| </div> | |||
| ), | |||
| }, | |||
| ]; | |||
| return ( | |||
| <div className={styles['service-info']}> | |||
| <PageTitle title="服务详情"></PageTitle> | |||
| <div className={styles['service-info__content']}> | |||
| <SubAreaTitle | |||
| title="基本信息" | |||
| image={require('@/assets/img/mirror-basic.png')} | |||
| style={{ marginBottom: '26px', flex: 'none' }} | |||
| ></SubAreaTitle> | |||
| <BasicInfo datas={basicInfo} labelWidth={66} style={{ flex: 'none' }}></BasicInfo> | |||
| <SubAreaTitle | |||
| title="服务版本" | |||
| image={require('@/assets/img/service-version.png')} | |||
| style={{ margin: '40px 0 26px', flex: 'none' }} | |||
| ></SubAreaTitle> | |||
| <div className={styles['service-info__content__filter']}> | |||
| <Input.Search | |||
| placeholder="按服务版本筛选" | |||
| onSearch={onSearch} | |||
| onChange={(e) => setInputText(e.target.value)} | |||
| style={{ width: 300 }} | |||
| value={inputText} | |||
| allowClear | |||
| /> | |||
| <Select | |||
| style={{ width: 100, marginLeft: '20px' }} | |||
| placeholder="请选择" | |||
| onChange={(value) => setServiceStatus(value ?? '')} | |||
| options={allServiceStatusOptions} | |||
| value={serviceStatus} | |||
| allowClear | |||
| ></Select> | |||
| <Button | |||
| style={{ marginRight: '20px', marginLeft: 'auto' }} | |||
| type="default" | |||
| onClick={() => createServiceVersion(ServiceOperationType.Create)} | |||
| icon={<KFIcon type="icon-xinjian2" />} | |||
| > | |||
| 新增版本 | |||
| </Button> | |||
| <Button | |||
| style={{ marginRight: 0 }} | |||
| type="default" | |||
| onClick={getServiceVersions} | |||
| icon={<KFIcon type="icon-shuaxin" />} | |||
| > | |||
| 刷新 | |||
| </Button> | |||
| </div> | |||
| <div | |||
| className={classNames('vertical-scroll-table', styles['service-info__content__table'])} | |||
| > | |||
| <Table | |||
| dataSource={tableData} | |||
| columns={columns} | |||
| scroll={{ y: 'calc(100% - 55px)' }} | |||
| pagination={{ | |||
| ...pagination, | |||
| total: total, | |||
| showSizeChanger: true, | |||
| showQuickJumper: true, | |||
| showTotal: () => `共${total}条`, | |||
| }} | |||
| onChange={handleTableChange} | |||
| rowKey="id" | |||
| /> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| ); | |||
| } | |||
| export default ServiceInfo; | |||
| @@ -1,4 +1,4 @@ | |||
| .model-deployment-info { | |||
| .service-version-info { | |||
| height: 100%; | |||
| &__content { | |||
| @@ -1,19 +1,20 @@ | |||
| /* | |||
| * @Author: 赵伟 | |||
| * @Date: 2024-04-16 13:58:08 | |||
| * @Description: 镜像详情 | |||
| * @Description: 服务版本详情 | |||
| */ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import PageTitle from '@/components/PageTitle'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { useSessionStorage } from '@/hooks/sessionStorage'; | |||
| import { modelDeploymentInfoKey } from '@/utils/sessionStorage'; | |||
| import { getServiceVersionInfoReq } from '@/services/modelDeployment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { useParams } from '@umijs/max'; | |||
| import { Tabs, type TabsProps } from 'antd'; | |||
| import { useState } from 'react'; | |||
| import { useEffect, useState } from 'react'; | |||
| import BasicInfo from '../components/BasicInfo'; | |||
| import ServerLog from '../components/ServerLog'; | |||
| import UserGuide from '../components/UserGuide'; | |||
| import { ModelDeploymentData } from '../types'; | |||
| import { ServiceVersionData } from '../types'; | |||
| import styles from './index.less'; | |||
| export enum ModelDeploymentTabKey { | |||
| @@ -22,13 +23,23 @@ export enum ModelDeploymentTabKey { | |||
| Log = 'Log', // 服务日志 | |||
| } | |||
| function ModelDeploymentInfo() { | |||
| function ServiceVersionInfo() { | |||
| const [activeTab, setActiveTab] = useState<string>(ModelDeploymentTabKey.Predict); | |||
| const [modelDeployementInfo] = useSessionStorage<ModelDeploymentData | undefined>( | |||
| modelDeploymentInfoKey, | |||
| true, | |||
| undefined, | |||
| ); | |||
| const [versionInfo, setVersionInfo] = useState<ServiceVersionData | undefined>(undefined); | |||
| const params = useParams(); | |||
| const id = params.id; | |||
| useEffect(() => { | |||
| getServiceVersionInfo(); | |||
| }, []); | |||
| // 获取服务版本详情 | |||
| const getServiceVersionInfo = async () => { | |||
| const [res] = await to(getServiceVersionInfoReq(id)); | |||
| if (res && res.data) { | |||
| setVersionInfo(res.data); | |||
| } | |||
| }; | |||
| const tabItems = [ | |||
| { | |||
| @@ -40,13 +51,13 @@ function ModelDeploymentInfo() { | |||
| key: ModelDeploymentTabKey.Guide, | |||
| label: '调用指南', | |||
| icon: <KFIcon type="icon-tiaoyongzhinan" />, | |||
| children: <UserGuide info={modelDeployementInfo}></UserGuide>, | |||
| children: <UserGuide info={versionInfo}></UserGuide>, | |||
| }, | |||
| { | |||
| key: ModelDeploymentTabKey.Log, | |||
| label: '服务日志', | |||
| icon: <KFIcon type="icon-fuwurizhi" />, | |||
| children: <ServerLog info={modelDeployementInfo}></ServerLog>, | |||
| children: <ServerLog info={versionInfo}></ServerLog>, | |||
| }, | |||
| ]; | |||
| @@ -56,16 +67,16 @@ function ModelDeploymentInfo() { | |||
| }; | |||
| return ( | |||
| <div className={styles['model-deployment-info']}> | |||
| <PageTitle title="服务详情"></PageTitle> | |||
| <div className={styles['model-deployment-info__content']}> | |||
| <div className={styles['service-version-info']}> | |||
| <PageTitle title="服务版本详情"></PageTitle> | |||
| <div className={styles['service-version-info__content']}> | |||
| <SubAreaTitle | |||
| title="基本信息" | |||
| image={require('@/assets/img/mirror-basic.png')} | |||
| style={{ marginBottom: '26px' }} | |||
| ></SubAreaTitle> | |||
| <BasicInfo info={modelDeployementInfo} /> | |||
| <div className={styles['model-deployment-info__content__tabs']}> | |||
| <BasicInfo info={versionInfo} /> | |||
| <div className={styles['service-version-info__content__tabs']}> | |||
| <Tabs activeKey={activeTab} items={tabItems} onChange={hanleTabChange} /> | |||
| </div> | |||
| </div> | |||
| @@ -73,4 +84,4 @@ function ModelDeploymentInfo() { | |||
| ); | |||
| } | |||
| export default ModelDeploymentInfo; | |||
| export default ServiceVersionInfo; | |||
| @@ -1,12 +1,13 @@ | |||
| import LabelValue from '@/components/LabelValue'; | |||
| import { useComputingResource } from '@/hooks/resource'; | |||
| import { ModelDeploymentData } from '@/pages/ModelDeployment/types'; | |||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { Link } from '@umijs/max'; | |||
| import { Col, Row } from 'antd'; | |||
| import ModelDeploymentStatusCell from '../ModelDeployStatusCell'; | |||
| import ServiceRunStatusCell from '../ModelDeployStatusCell'; | |||
| type BasicInfoProps = { | |||
| info?: ModelDeploymentData; | |||
| info?: ServiceVersionData; | |||
| }; | |||
| function BasicInfo({ info }: BasicInfoProps) { | |||
| @@ -14,42 +15,75 @@ function BasicInfo({ info }: BasicInfoProps) { | |||
| // 格式化环境变量 | |||
| const formatEnvText = () => { | |||
| if (!info?.env) { | |||
| if (!info?.env_variables) { | |||
| return '--'; | |||
| } | |||
| const env = info.env; | |||
| const env = info.env_variables; | |||
| return Object.entries(env) | |||
| .map(([key, value]) => `${key}: ${value}`) | |||
| .join('\n'); | |||
| }; | |||
| const formatCodeConfig = () => { | |||
| if (info && info.code_config) { | |||
| const url = `${info.code_config.code_path}/tree/${info.code_config.branch}`; | |||
| return ( | |||
| <a href={url} target="_blank" rel="noreferrer"> | |||
| {info?.code_config?.show_value} | |||
| </a> | |||
| ); | |||
| } | |||
| return undefined; | |||
| }; | |||
| const formatResource = () => { | |||
| if (info && info.resource) { | |||
| return getResourceDescription(info.resource); | |||
| } | |||
| return undefined; | |||
| }; | |||
| const formatModel = () => { | |||
| if (info && info.model) { | |||
| const model = info.model; | |||
| const path = `/dataset/model/info/${model.id}?version=${model.version}&name=${model.name}&owner=${model.owner}&identifier=${model.identifier}`; | |||
| return <Link to={path}>{info?.model?.show_value}</Link>; | |||
| } | |||
| return undefined; | |||
| }; | |||
| return ( | |||
| <div> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| <Col span={10}> | |||
| <LabelValue label="服务名称:" value={info?.service_name}></LabelValue> | |||
| </Col> | |||
| <Col span={10}> | |||
| <LabelValue label="版本名称:" value={info?.version}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| <Col span={10}> | |||
| <LabelValue label="代码配置" value={formatCodeConfig()}></LabelValue> | |||
| </Col> | |||
| <Col span={10}> | |||
| <LabelValue label="镜 像:" value={info?.image}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| <Col span={10}> | |||
| <LabelValue | |||
| label="状 态:" | |||
| value={ModelDeploymentStatusCell(info?.status)} | |||
| ></LabelValue> | |||
| <LabelValue label="状 态:" value={ServiceRunStatusCell(info?.run_state)}></LabelValue> | |||
| </Col> | |||
| <Col span={10}> | |||
| <LabelValue label="模 型:" value={info?.model?.show_value}></LabelValue> | |||
| <LabelValue label="模 型:" value={formatModel()}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| <Col span={10}> | |||
| <LabelValue label="创建人:" value={info?.created_by}></LabelValue> | |||
| <LabelValue label="资源规格:" value={formatResource()}></LabelValue> | |||
| </Col> | |||
| <Col span={10}> | |||
| <LabelValue label="挂载路径:" value={info?.model_path}></LabelValue> | |||
| <LabelValue label="挂载路径:" value={info?.mount_path}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| @@ -68,19 +102,11 @@ function BasicInfo({ info }: BasicInfoProps) { | |||
| <LabelValue label="更新时间:" value={formatDate(info?.update_time)}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40} style={{ marginBottom: '20px' }}> | |||
| <Row gutter={40}> | |||
| <Col span={10}> | |||
| <LabelValue label="环境变量:" value={formatEnvText()}></LabelValue> | |||
| </Col> | |||
| <Col span={10}> | |||
| <LabelValue | |||
| label="资源规格:" | |||
| value={info?.resource ? getResourceDescription(info.resource) : '--'} | |||
| ></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| <Row gutter={40}> | |||
| <Col span={18}> | |||
| <LabelValue label="描 述:" value={info?.description}></LabelValue> | |||
| </Col> | |||
| </Row> | |||
| @@ -3,42 +3,42 @@ | |||
| * @Date: 2024-04-18 18:35:41 | |||
| * @Description: 模型部署状态 | |||
| */ | |||
| import { ModelDeploymentStatus } from '@/enums'; | |||
| import { ServiceRunStatus } from '@/enums'; | |||
| import styles from './index.less'; | |||
| export type ModelDeploymentStatusInfo = { | |||
| export type ServiceRunStatusInfo = { | |||
| text: string; | |||
| classname: string; | |||
| }; | |||
| export const statusInfo: Record<ModelDeploymentStatus, ModelDeploymentStatusInfo> = { | |||
| [ModelDeploymentStatus.Init]: { | |||
| export const statusInfo: Record<ServiceRunStatus, ServiceRunStatusInfo> = { | |||
| [ServiceRunStatus.Init]: { | |||
| text: '启动中', | |||
| classname: styles['model-deployment-status-cell'], | |||
| }, | |||
| [ModelDeploymentStatus.Running]: { | |||
| [ServiceRunStatus.Running]: { | |||
| classname: styles['model-deployment-status-cell--running'], | |||
| text: '运行中', | |||
| }, | |||
| [ModelDeploymentStatus.Stopped]: { | |||
| [ServiceRunStatus.Stopped]: { | |||
| classname: styles['model-deployment-status-cell--stopped'], | |||
| text: '已停止', | |||
| }, | |||
| [ModelDeploymentStatus.Failed]: { | |||
| [ServiceRunStatus.Failed]: { | |||
| classname: styles['model-deployment-status-cell--error'], | |||
| text: '失败', | |||
| }, | |||
| [ModelDeploymentStatus.Pending]: { | |||
| [ServiceRunStatus.Pending]: { | |||
| classname: styles['model-deployment-status-cell--pending'], | |||
| text: '挂起中', | |||
| }, | |||
| }; | |||
| function ModelDeploymentStatusCell(status?: ModelDeploymentStatus | null) { | |||
| function ServiceRunStatusCell(status?: ServiceRunStatus | null) { | |||
| if (status === null || status === undefined || !statusInfo[status]) { | |||
| return <span>--</span>; | |||
| } | |||
| return <span className={statusInfo[status].classname}>{statusInfo[status].text}</span>; | |||
| } | |||
| export default ModelDeploymentStatusCell; | |||
| export default ServiceRunStatusCell; | |||
| @@ -1,10 +1,9 @@ | |||
| import { ModelDeploymentData } from '@/pages/ModelDeployment/types'; | |||
| import { getModelDeploymentLogReq } from '@/services/modelDeployment'; | |||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | |||
| import { getServiceVersionLogReq } from '@/services/modelDeployment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { DoubleRightOutlined } from '@ant-design/icons'; | |||
| import { Button, DatePicker, type TimeRangePickerProps } from 'antd'; | |||
| import dayjs from 'dayjs'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| const { RangePicker } = DatePicker; | |||
| @@ -28,7 +27,7 @@ type LogData = { | |||
| }; | |||
| type ServerLogProps = { | |||
| info?: ModelDeploymentData; | |||
| info?: ServiceVersionData; | |||
| }; | |||
| function ServerLog({ info }: ServerLogProps) { | |||
| @@ -64,9 +63,9 @@ function ServerLog({ info }: ServerLogProps) { | |||
| const params = { | |||
| start_time: logTime[0], | |||
| end_time: logTime[1], | |||
| ...pick(info, ['service_id', 'service_ins_id']), | |||
| id: info.id, | |||
| }; | |||
| const [res] = await to(getModelDeploymentLogReq(params)); | |||
| const [res] = await to(getServiceVersionLogReq(params)); | |||
| if (res && res.data) { | |||
| setLogData((prev) => [...prev, res.data]); | |||
| setHasMore(!!res.data.log_content); | |||
| @@ -1,12 +1,11 @@ | |||
| import { ModelDeploymentData } from '@/pages/ModelDeployment/types'; | |||
| import { getModelDeploymentDocsReq } from '@/services/modelDeployment'; | |||
| import { ServiceVersionData } from '@/pages/ModelDeployment/types'; | |||
| import { getServiceVersionDocsReq } from '@/services/modelDeployment'; | |||
| import { to } from '@/utils/promise'; | |||
| import { pick } from 'lodash'; | |||
| import { useEffect, useState } from 'react'; | |||
| import styles from './index.less'; | |||
| type UserGuideProps = { | |||
| info?: ModelDeploymentData; | |||
| info?: ServiceVersionData; | |||
| }; | |||
| function UserGuide({ info }: UserGuideProps) { | |||
| @@ -18,8 +17,7 @@ function UserGuide({ info }: UserGuideProps) { | |||
| // 获取模型部署文档 | |||
| const getModelDeploymentDocs = async () => { | |||
| if (info) { | |||
| const params = pick(info, ['service_id', 'service_ins_id']); | |||
| const [res] = await to(getModelDeploymentDocsReq(params)); | |||
| const [res] = await to(getServiceVersionDocsReq(info.id)); | |||
| if (res && res.data && res.data.docs) { | |||
| setDocs(JSON.stringify(res.data.docs, null, 2)); | |||
| } | |||
| @@ -1,31 +1,58 @@ | |||
| import { ModelDeploymentStatus } from '@/enums'; | |||
| import { ServiceRunStatus } from '@/enums'; | |||
| // 模型部署列表数据类型 | |||
| export type ModelDeploymentData = { | |||
| service_id: number; | |||
| service_ins_id: number; | |||
| service_name: string; | |||
| description: string; | |||
| status: ModelDeploymentStatus; | |||
| update_time: string; | |||
| create_time: string; | |||
| // 服务列表数据类型 | |||
| export type ServiceData = { | |||
| id: number; // 服务id | |||
| service_name: string; // 服务名称 | |||
| service_type: string; // 服务类型 | |||
| service_type_name: string; // 服务类型中文 | |||
| description: string; // 描述 | |||
| version_count: number; // 版本数量 | |||
| created_by: string; | |||
| model_path: string; | |||
| url: string; | |||
| image: string; | |||
| replicas: number; | |||
| resource: string; | |||
| create_time: string; | |||
| update_by: string; | |||
| update_time: string; | |||
| }; | |||
| // 服务版本数据类型 | |||
| export type ServiceVersionData = { | |||
| id: number; // 版本id | |||
| service_id: number; // 服务id | |||
| service_name: string; // 服务名称 | |||
| description: string; // 版本描述 | |||
| version: string; // 版本 | |||
| run_state: ServiceRunStatus; // 运行状态 | |||
| image: string; // 镜像 | |||
| replicas: number; // 副本数 | |||
| resource: string; // 资源 | |||
| mount_path: string; // 挂载路径 | |||
| model: { | |||
| // 模型 | |||
| id: number; | |||
| name: string; | |||
| version: string; | |||
| path: string; | |||
| identifier: string; | |||
| owner: string; | |||
| show_value: string; | |||
| }; | |||
| env: Record<string, string>; | |||
| code_config: { | |||
| // 代码配置 | |||
| code_path: string; | |||
| branch: string; | |||
| show_value: string; | |||
| }; | |||
| env_variables: Record<string, string>; // 环境变量 | |||
| url: string; // API URL | |||
| deployment_name: string; | |||
| update_by: string; | |||
| update_time: string; | |||
| create_time: string; | |||
| created_by: string; | |||
| }; | |||
| // 操作类型 | |||
| export enum ModelDeploymentOperationType { | |||
| export enum ServiceOperationType { | |||
| Create = 'Create', // 创建 | |||
| Update = 'Update', // 更新 | |||
| Restart = 'Restart', // 重启 | |||
| @@ -433,7 +433,7 @@ const EditPipeline = () => { | |||
| fitView: true, | |||
| minZoom: 0.5, | |||
| maxZoom: 5, | |||
| fitViewPadding: 300, | |||
| fitViewPadding: 200, | |||
| modes: { | |||
| default: [ | |||
| // config the shouldBegin for drag-node to avoid node moving while dragging on the anchor-point circles | |||
| @@ -15,6 +15,8 @@ import { useEffect, useState } from 'react'; | |||
| import CodeConfigItem from '../CodeConfigItem'; | |||
| import styles from './index.less'; | |||
| export { type CodeConfigData }; | |||
| export interface CodeSelectorModalProps extends Omit<ModalProps, 'onOk'> { | |||
| onOk?: (params: CodeConfigData | undefined) => void; | |||
| } | |||
| @@ -136,7 +136,6 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| const { close } = openAntdModal(CodeSelectorModal, { | |||
| onOk: (res) => { | |||
| if (res) { | |||
| console.log('res', res); | |||
| const value = JSON.stringify({ | |||
| id: res.id, | |||
| name: res.code_repo_name, | |||
| @@ -5,73 +5,102 @@ | |||
| */ | |||
| import { request } from '@umijs/max'; | |||
| // 分页查询模型部署列表 | |||
| export function getModelDeploymentListReq(data: any) { | |||
| return request(`/api/v1/model/get`, { | |||
| // 分页查询服务列表 | |||
| export function getServiceListReq(data: any) { | |||
| return request(`/api/mmp/service`, { | |||
| method: 'GET', | |||
| params: data, | |||
| }); | |||
| } | |||
| // 创建推理服务 | |||
| export function createServiceReq(data: any) { | |||
| return request(`/api/mmp/service`, { | |||
| method: 'POST', | |||
| data, | |||
| }); | |||
| } | |||
| // 查询模型部署详情 | |||
| export function getModelDeploymentInfoReq(id: number) { | |||
| return request(`/api/mmp/image/${id}`, { | |||
| // 编辑推理服务 | |||
| export function updateServiceReq(data: any) { | |||
| return request(`/api/mmp/service`, { | |||
| method: 'PUT', | |||
| data, | |||
| }); | |||
| } | |||
| // 删除推理服务 | |||
| export function deleteServiceReq(id: any) { | |||
| return request(`/api/mmp/service/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 获取服务详情 | |||
| export function getServiceInfoReq(id: any) { | |||
| return request(`/api/mmp/service/serviceDetail/${id}`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 创建模型部署 | |||
| export function createModelDeploymentReq(data: any) { | |||
| return request(`/api/v1/model/create`, { | |||
| method: 'POST', | |||
| data, | |||
| // ------------------------------- 服务版本部分 --------------------------------------------- | |||
| // 获取服务版本列表 | |||
| export function getServiceVersionsReq(data: any) { | |||
| return request(`/api/mmp/service/serviceVersion`, { | |||
| method: 'GET', | |||
| params: data, | |||
| }); | |||
| } | |||
| // 删除模型部署 | |||
| export function deleteModelDeploymentReq(data: any) { | |||
| return request(`/api/v1/model/delete`, { | |||
| // 创建服务版本 | |||
| export function createServiceVersionReq(data: any) { | |||
| return request(`/api/mmp/service/serviceVersion`, { | |||
| method: 'POST', | |||
| data, | |||
| }); | |||
| } | |||
| // 重启模型部署 | |||
| export function restartModelDeploymentReq(data: any) { | |||
| return request(`/api/v1/model/restart`, { | |||
| method: 'POST', | |||
| // 更新服务版本 | |||
| export function updateServiceVersionReq(data: any) { | |||
| return request(`/api/mmp/service/serviceVersion`, { | |||
| method: 'PUT', | |||
| data, | |||
| }); | |||
| } | |||
| // 停止模型部署 | |||
| export function stopModelDeploymentReq(data: any) { | |||
| return request(`/api/v1/model/stop`, { | |||
| method: 'POST', | |||
| data, | |||
| // 删除服务版本 | |||
| export function deleteServiceVersionReq(id: any) { | |||
| return request(`/api/mmp/service/serviceVersion/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 更新模型部署 | |||
| export function updateModelDeploymentReq(data: any) { | |||
| return request(`/api/v1/model/update`, { | |||
| method: 'POST', | |||
| data, | |||
| // 获取服务版本详情 | |||
| export function getServiceVersionInfoReq(id: any) { | |||
| return request(`/api/mmp/service/serviceVersionDetail/${id}`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 获取模型部署操作指南 | |||
| export function getModelDeploymentDocsReq(data: any) { | |||
| return request(`/api/v1/model/getDocs`, { | |||
| method: 'POST', | |||
| data, | |||
| // 停止服务版本 | |||
| export function stopServiceVersionReq(id: any) { | |||
| return request(`/api/mmp/service/stopServiceVersion/${id}`, { | |||
| method: 'DELETE', | |||
| }); | |||
| } | |||
| // 获取模型部署日志 | |||
| export function getModelDeploymentLogReq(data: any) { | |||
| return request(`/api/v1/model/getAppLog`, { | |||
| method: 'POST', | |||
| data, | |||
| // 获取服务版本操作指南 | |||
| export function getServiceVersionDocsReq(id: any) { | |||
| return request(`/api/mmp/service/getServiceVersionDocs/${id}`, { | |||
| method: 'GET', | |||
| }); | |||
| } | |||
| // 获取服务版本日志 | |||
| export function getServiceVersionLogReq(params: any) { | |||
| return request(`/api/mmp/service/getServiceVersionLog`, { | |||
| method: 'GET', | |||
| params, | |||
| }); | |||
| } | |||
| @@ -1,7 +1,9 @@ | |||
| // 用于新建镜像 | |||
| export const mirrorNameKey = 'mirror-name'; | |||
| // 模型部署 | |||
| export const modelDeploymentInfoKey = 'model-deployment-info'; | |||
| // 模型部署服务 | |||
| export const serviceInfoKey = 'service-info'; | |||
| // 模型部署服务版本 | |||
| export const serviceVersionInfoKey = 'service-version-info'; | |||
| // 编辑器 url | |||
| export const editorUrlKey = 'editor-url'; | |||
| // 数据集、模型资源 | |||