import { useCallback, useContext, useEffect, useState } from "react";
|
import { NavLink } from "react-router-dom";
|
import { Button, Typography, Spinner, EmptyState, SimpleCard } from "@humansignal/ui";
|
import { useUpdatePageTitle, createTitleFromSegments } from "@humansignal/core";
|
import { Form, Label, Toggle } from "../../../components/Form";
|
import { modal } from "../../../components/Modal/Modal";
|
import { IconModels, IconExternal } from "@humansignal/icons";
|
import { useAPI } from "../../../providers/ApiProvider";
|
import { ProjectContext } from "../../../providers/ProjectProvider";
|
import { MachineLearningList } from "./MachineLearningList";
|
import { CustomBackendForm } from "./Forms";
|
import { TestRequest } from "./TestRequest";
|
import { StartModelTraining } from "./StartModelTraining";
|
import "./MachineLearningSettings.scss";
|
|
export const MachineLearningSettings = () => {
|
const api = useAPI();
|
const { project, fetchProject } = useContext(ProjectContext);
|
const [backends, setBackends] = useState([]);
|
const [loading, setLoading] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
|
useUpdatePageTitle(createTitleFromSegments([project?.title, "模型设置"]));
|
|
const fetchBackends = useCallback(async () => {
|
setLoading(true);
|
const models = await api.callApi("mlBackends", {
|
params: {
|
project: project.id,
|
include_static: true,
|
},
|
});
|
|
if (models) setBackends(models);
|
setLoading(false);
|
setLoaded(true);
|
}, [project, setBackends]);
|
|
const startTrainingModal = useCallback(
|
(backend) => {
|
const modalProps = {
|
title: "开始模型训练",
|
style: { width: 760 },
|
closeOnClickOutside: true,
|
body: <StartModelTraining backend={backend} />,
|
};
|
|
modal(modalProps);
|
},
|
[project],
|
);
|
|
const showRequestModal = useCallback(
|
(backend) => {
|
const modalProps = {
|
title: "测试请求",
|
style: { width: 760 },
|
closeOnClickOutside: true,
|
body: <TestRequest backend={backend} />,
|
};
|
|
modal(modalProps);
|
},
|
[project],
|
);
|
|
const showMLFormModal = useCallback(
|
(backend) => {
|
const action = backend ? "updateMLBackend" : "addMLBackend";
|
const modalProps = {
|
title: `${backend ? "编辑" : "连接"} 模型`,
|
style: { width: 760 },
|
closeOnClickOutside: false,
|
body: (
|
<CustomBackendForm
|
action={action}
|
backend={backend}
|
project={project}
|
onSubmit={() => {
|
fetchBackends();
|
modalRef.close();
|
}}
|
/>
|
),
|
};
|
|
const modalRef = modal(modalProps);
|
},
|
[project, fetchBackends],
|
);
|
|
useEffect(() => {
|
if (project.id) {
|
fetchBackends();
|
}
|
}, [project.id]);
|
|
return (
|
<section>
|
<div className="w-[42rem]">
|
<Typography variant="headline" size="medium" className="mb-base">
|
模型
|
</Typography>
|
{loading && <Spinner size={32} />}
|
{loaded && backends.length === 0 && (
|
<SimpleCard title="" className="bg-primary-background border-primary-border-subtler p-base">
|
<EmptyState
|
size="medium"
|
variant="primary"
|
icon={<IconModels />}
|
title="连接您的第一个模型"
|
description="连接机器学习模型以生成项目的实时预测。比较预测结果,利用自动预标注加速标注,并通过主动学习引导团队处理最具影响力的任务。"
|
actions={
|
<Button
|
variant="primary"
|
look="filled"
|
onClick={() => showMLFormModal()}
|
aria-label="添加机器学习模型"
|
>
|
连接模型
|
</Button>
|
}
|
footer={
|
!window.APP_SETTINGS?.whitelabel_is_active && (
|
<Typography variant="label" size="small" className="text-primary-link">
|
<a
|
href="https://labelstud.io/guide/ml"
|
target="_blank"
|
rel="noopener noreferrer"
|
data-testid="ml-help-link"
|
aria-label="了解更多关于机器学习模型的信息(在新窗口打开)"
|
className="inline-flex items-center gap-1 hover:underline"
|
>
|
了解更多
|
<IconExternal width={16} height={16} />
|
</a>
|
</Typography>
|
)
|
}
|
/>
|
</SimpleCard>
|
)}
|
<MachineLearningList
|
onEdit={(backend) => showMLFormModal(backend)}
|
onTestRequest={(backend) => showRequestModal(backend)}
|
onStartTraining={(backend) => startTrainingModal(backend)}
|
fetchBackends={fetchBackends}
|
backends={backends}
|
/>
|
|
{backends.length > 0 && (
|
<div className="my-wide">
|
<Typography size="small" className="text-neutral-content-subtler">
|
检测到已连接的模型!如果您希望从此模型获取预测结果,请按照以下步骤操作:
|
</Typography>
|
<Typography size="small" className="text-neutral-content-subtler mt-base">
|
1. 导航到 <i>数据管理器</i>。
|
</Typography>
|
<Typography size="small" className="text-neutral-content-subtler mt-tighter">
|
2. 选择所需的任务。
|
</Typography>
|
<Typography size="small" className="text-neutral-content-subtler mt-tighter">
|
3. 从 <i>操作</i> 菜单中点击 <i>批量预测</i>。
|
</Typography>
|
<Typography size="small" className="text-neutral-content-subtler mt-base">
|
如果您想使用模型预测进行预标注,请在{" "}
|
<NavLink to="annotation" className="hover:underline">
|
标注设置
|
</NavLink>
|
中进行配置。
|
</Typography>
|
</div>
|
)}
|
|
<Form
|
action="updateProject"
|
formData={{ ...project }}
|
params={{ pk: project.id }}
|
onSubmit={() => fetchProject()}
|
>
|
{backends.length > 0 && (
|
<div className="p-wide border border-neutral-border rounded-md">
|
<Form.Row columnCount={1}>
|
<Label text="配置" large />
|
|
<div>
|
<Toggle
|
label="在提交标注时开始模型训练"
|
description="此选项将发送包含标注信息的请求到 /train。您可以使用此功能启用主动学习循环。您也可以通过模型卡片中的菜单手动开始训练。"
|
name="start_training_on_annotation_update"
|
/>
|
</div>
|
</Form.Row>
|
</div>
|
)}
|
|
{backends.length > 0 && (
|
<Form.Actions>
|
<Form.Indicator>
|
<span case="success">已保存!</span>
|
</Form.Indicator>
|
<Button type="submit" look="primary" className="w-[120px]" aria-label="保存机器学习设置">
|
保存
|
</Button>
|
</Form.Actions>
|
)}
|
</Form>
|
</div>
|
</section>
|
);
|
};
|
|
MachineLearningSettings.title = "模型";
|
MachineLearningSettings.path = "/ml";
|