You've already forked DataMate
feat: add ModelAccess to settings page (#29)
* feat: Update site name to DataMate and refine text for AI data processing * feat: Refactor settings page and implement model access functionality - Created a new ModelAccess component for managing model configurations. - Removed the old Settings component and replaced it with a new SettingsPage component that integrates ModelAccess, SystemConfig, and WebhookConfig. - Added SystemConfig component for managing system settings. - Implemented WebhookConfig component for managing webhook configurations. - Updated API functions for model management in settings.apis.ts. - Adjusted routing to point to the new SettingsPage component.
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/huawei-logo.webp" />
|
<link rel="icon" type="image/svg+xml" href="/huawei-logo.webp" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>ML Dataset Tool</title>
|
<title>DataMate</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ const { addMockPrefix } = require("./mock-core/util.cjs");
|
|||||||
|
|
||||||
const MockAPI = {
|
const MockAPI = {
|
||||||
// 数据归集接口
|
// 数据归集接口
|
||||||
queryTasksUsingPost: "/data-collection/tasks", // 获取数据源任务列表
|
queryTasksUsingGet: "/data-collection/tasks", // 获取数据源任务列表
|
||||||
createTaskUsingPost: "/data-collection/tasks/create", // 创建数据源任务
|
createTaskUsingPost: "/data-collection/tasks/create", // 创建数据源任务
|
||||||
queryTaskByIdUsingGet: "/data-collection/tasks/:id", // 根据ID获取数据源任务详情
|
queryTaskByIdUsingGet: "/data-collection/tasks/:id", // 根据ID获取数据源任务详情
|
||||||
updateTaskByIdUsingPut: "/data-collection/tasks/:id", // 更新数据源任务
|
updateTaskByIdUsingPut: "/data-collection/tasks/:id", // 更新数据源任务
|
||||||
|
queryDataXTemplatesUsingGet: "/data-collection/templates", // 获取DataX数据源模板列表
|
||||||
deleteTaskByIdUsingDelete: "/data-collection/tasks/:id", // 删除数据源任务
|
deleteTaskByIdUsingDelete: "/data-collection/tasks/:id", // 删除数据源任务
|
||||||
executeTaskByIdUsingPost: "/data-collection/tasks/:id/execute", // 执行数据源任务
|
executeTaskByIdUsingPost: "/data-collection/tasks/:id/execute", // 执行数据源任务
|
||||||
stopTaskByIdUsingPost: "/data-collection/tasks/:id/stop", // 停止数据源任务
|
stopTaskByIdUsingPost: "/data-collection/tasks/:id/stop", // 停止数据源任务
|
||||||
@@ -91,7 +92,6 @@ const MockAPI = {
|
|||||||
deleteInstructionTemplateByIdUsingDelete: "/synthesis/templates/:templateId", // 删除指令模板
|
deleteInstructionTemplateByIdUsingDelete: "/synthesis/templates/:templateId", // 删除指令模板
|
||||||
instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调
|
instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调
|
||||||
cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏
|
cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏
|
||||||
queryOperatorsUsingPost: "/synthesis/operators", // 获取操作列表
|
|
||||||
|
|
||||||
// 数据评测接口
|
// 数据评测接口
|
||||||
queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表
|
queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表
|
||||||
@@ -144,6 +144,14 @@ const MockAPI = {
|
|||||||
deleteOperatorByIdUsingDelete: "/operators/:operatorId", // 删除算子
|
deleteOperatorByIdUsingDelete: "/operators/:operatorId", // 删除算子
|
||||||
publishOperatorUsingPost: "/operators/:operatorId/publish", // 发布算子
|
publishOperatorUsingPost: "/operators/:operatorId/publish", // 发布算子
|
||||||
unpublishOperatorUsingPost: "/operators/:operatorId/unpublish", // 下架算子
|
unpublishOperatorUsingPost: "/operators/:operatorId/unpublish", // 下架算子
|
||||||
|
|
||||||
|
// 设置接口
|
||||||
|
queryModelsUsingGet: "/models/list", // 获取模型列表
|
||||||
|
queryProvidersUsingGet: "/models/providers", // 获取模型提供商列表
|
||||||
|
createModelUsingPost: "/models/create", // 创建模型
|
||||||
|
updateModelUsingPut: "/models/:id", // 更新模型
|
||||||
|
deleteModelUsingDelete: "/models/:id", // 删除模型
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = addMockPrefix("/api", MockAPI);
|
module.exports = addMockPrefix("/api", MockAPI);
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ function datasetItem() {
|
|||||||
return {
|
return {
|
||||||
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
name: Mock.Random.ctitle(5, 20),
|
name: Mock.Random.ctitle(5, 20),
|
||||||
type: Mock.Random.pick(["TEXT", "IMAGE", "AUDIO", "VIDEO"]),
|
datasetType: Mock.Random.pick(["TEXT", "IMAGE", "AUDIO", "VIDEO"]),
|
||||||
status: Mock.Random.pick(["ACTIVE", "INACTIVE", "PROCESSING"]),
|
status: Mock.Random.pick(["DRAFT","ACTIVE", "INACTIVE", "PROCESSING"]),
|
||||||
tags: Mock.Random.shuffle(tagList).slice(0, Mock.Random.integer(1, 3)),
|
tags: Mock.Random.shuffle(tagList).slice(0, Mock.Random.integer(1, 3)),
|
||||||
totalSize: Mock.Random.integer(1024, 1024 * 1024 * 1024), // in bytes
|
totalSize: Mock.Random.integer(1024, 1024 * 1024 * 1024), // in bytes
|
||||||
description: Mock.Random.cparagraph(1, 3),
|
description: Mock.Random.cparagraph(1, 3),
|
||||||
@@ -164,7 +164,7 @@ module.exports = function (router) {
|
|||||||
console.log("filter type:", type);
|
console.log("filter type:", type);
|
||||||
|
|
||||||
filteredDatasets = filteredDatasets.filter(
|
filteredDatasets = filteredDatasets.filter(
|
||||||
(dataset) => dataset.type === type
|
(dataset) => dataset.datasetType === type
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (status) {
|
if (status) {
|
||||||
|
|||||||
177
frontend/src/mock/mock-seed/settings.cjs
Normal file
177
frontend/src/mock/mock-seed/settings.cjs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
const Mock = require("mockjs");
|
||||||
|
const API = require("../mock-apis.cjs");
|
||||||
|
|
||||||
|
// 算子标签数据
|
||||||
|
function ModelItem() {
|
||||||
|
return {
|
||||||
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
modelName: Mock.Random.pick([
|
||||||
|
"数据清洗",
|
||||||
|
"特征选择",
|
||||||
|
"分类算法",
|
||||||
|
"聚类算法",
|
||||||
|
"回归分析",
|
||||||
|
"深度神经网络",
|
||||||
|
"卷积神经网络",
|
||||||
|
"循环神经网络",
|
||||||
|
"注意力机制",
|
||||||
|
"文本分析",
|
||||||
|
"图像处理",
|
||||||
|
"语音识别",
|
||||||
|
"推荐算法",
|
||||||
|
"异常检测",
|
||||||
|
"优化算法",
|
||||||
|
"集成学习",
|
||||||
|
"迁移学习",
|
||||||
|
"强化学习",
|
||||||
|
"联邦学习",
|
||||||
|
]),
|
||||||
|
provider: Mock.Random.pick([
|
||||||
|
"OpenAI",
|
||||||
|
"Anthropic",
|
||||||
|
"Cohere",
|
||||||
|
"AI21 Labs",
|
||||||
|
"Hugging Face",
|
||||||
|
"Google Cloud AI",
|
||||||
|
"Microsoft Azure AI",
|
||||||
|
"Amazon Web Services AI",
|
||||||
|
"IBM Watson",
|
||||||
|
"Alibaba Cloud AI",
|
||||||
|
]),
|
||||||
|
type: Mock.Random.pick(["CHAT", "EMBEDDING"]),
|
||||||
|
usageCount: Mock.Random.integer(1, 500),
|
||||||
|
createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
updatedAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const modelList = new Array(50).fill(null).map(ModelItem);
|
||||||
|
|
||||||
|
function ProviderItem(provider) {
|
||||||
|
return {
|
||||||
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
provider,
|
||||||
|
baseUrl: Mock.Random.url("https") + "/v1/models",
|
||||||
|
createdAt: Mock.Random.datetime("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProviderList = [
|
||||||
|
"OpenAI",
|
||||||
|
"Anthropic",
|
||||||
|
"Cohere",
|
||||||
|
"AI21 Labs",
|
||||||
|
"Hugging Face",
|
||||||
|
"Google Cloud AI",
|
||||||
|
"Microsoft Azure AI",
|
||||||
|
"Amazon Web Services AI",
|
||||||
|
"IBM Watson",
|
||||||
|
"Alibaba Cloud AI",
|
||||||
|
].map(ProviderItem);
|
||||||
|
|
||||||
|
module.exports = function (router) {
|
||||||
|
// 获取模型列表
|
||||||
|
router.get(API.queryModelsUsingGet, (req, res) => {
|
||||||
|
const {
|
||||||
|
page = 0,
|
||||||
|
size = 20,
|
||||||
|
keyword = "",
|
||||||
|
provider = "",
|
||||||
|
type = "",
|
||||||
|
} = req.query;
|
||||||
|
|
||||||
|
let filteredModels = modelList;
|
||||||
|
|
||||||
|
if (keyword) {
|
||||||
|
filteredModels = modelList.filter((model) =>
|
||||||
|
model.modelName.toLowerCase().includes(keyword.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (provider && provider !== "all") {
|
||||||
|
filteredModels = filteredModels.filter(
|
||||||
|
(model) => model.provider === provider
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (type && type !== "all") {
|
||||||
|
filteredModels = filteredModels.filter((model) => model.type === type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startIndex = page * size;
|
||||||
|
const endIndex = startIndex + parseInt(size);
|
||||||
|
const pageData = filteredModels.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
res.status(201).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "Success",
|
||||||
|
data: {
|
||||||
|
content: pageData,
|
||||||
|
totalElements: filteredModels.length,
|
||||||
|
totalPages: Math.ceil(filteredModels.length / size),
|
||||||
|
size: parseInt(size),
|
||||||
|
number: parseInt(page),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取模型提供商列表
|
||||||
|
router.get(API.queryProvidersUsingGet, (req, res) => {
|
||||||
|
res.status(201).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "success",
|
||||||
|
data: ProviderList,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 创建模型
|
||||||
|
router.post(API.createModelUsingPost, (req, res) => {
|
||||||
|
const { ...modelData } = req.body;
|
||||||
|
const newModel = {
|
||||||
|
id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""),
|
||||||
|
...modelData,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
modelList.unshift(newModel);
|
||||||
|
res.status(201).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "success",
|
||||||
|
data: newModel,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 删除模型
|
||||||
|
router.delete(API.deleteModelUsingDelete, (req, res) => {
|
||||||
|
const { id } = req.params;
|
||||||
|
|
||||||
|
const index = modelList.findIndex((model) => model.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
modelList.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(204).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "success",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新模型
|
||||||
|
router.put(API.updateModelUsingPut, (req, res) => {
|
||||||
|
const { id, ...update } = req.params;
|
||||||
|
|
||||||
|
const index = modelList.findIndex((model) => model.id === id);
|
||||||
|
if (index !== -1) {
|
||||||
|
modelList[index] = {
|
||||||
|
...modelList[index],
|
||||||
|
...update,
|
||||||
|
updatedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
res.status(201).send({
|
||||||
|
code: "0",
|
||||||
|
msg: "success",
|
||||||
|
data: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -51,11 +51,11 @@ export default function DatasetManagementPage() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "文件总数",
|
title: "文件总数",
|
||||||
value: data?.totalFiles || "0 MB",
|
value: data?.totalFiles || 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "总大小",
|
title: "总大小",
|
||||||
value: formatBytes(data?.totalSize) || 0,
|
value: formatBytes(data?.totalSize) || '0 B',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
count: [
|
count: [
|
||||||
|
|||||||
@@ -29,8 +29,7 @@ export default function WelcomePage() {
|
|||||||
<span className="text-blue-600"> AI数据集</span>
|
<span className="text-blue-600"> AI数据集</span>
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-8">
|
<p className="text-xl text-gray-600 max-w-3xl mx-auto mb-8">
|
||||||
从数据管理到知识生成,一站式解决机器学习数据准备的所有需求。
|
从数据管理到知识生成,一站式解决企业AI数据处理的场景问题。
|
||||||
支持对话式操作、智能编排、数据合成、智能标注、全面评估和RAG知识库构建。
|
|
||||||
</p>
|
</p>
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
||||||
<span
|
<span
|
||||||
|
|||||||
375
frontend/src/pages/SettingsPage/ModelAccess.tsx
Normal file
375
frontend/src/pages/SettingsPage/ModelAccess.tsx
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Button,
|
||||||
|
Form,
|
||||||
|
Input,
|
||||||
|
Modal,
|
||||||
|
Select,
|
||||||
|
Table,
|
||||||
|
Tooltip,
|
||||||
|
Popconfirm,
|
||||||
|
message,
|
||||||
|
} from "antd";
|
||||||
|
import {
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { SearchControls } from "@/components/SearchControls";
|
||||||
|
import useFetchData from "@/hooks/useFetchData";
|
||||||
|
import {
|
||||||
|
createModelUsingPost,
|
||||||
|
deleteModelByIdUsingDelete,
|
||||||
|
queryModelListUsingGet,
|
||||||
|
queryModelProvidersUsingGet,
|
||||||
|
updateModelByIdUsingPut,
|
||||||
|
} from "./settings.apis";
|
||||||
|
|
||||||
|
interface ModelI {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
provider: string;
|
||||||
|
model: string;
|
||||||
|
apiKey: string;
|
||||||
|
isEnabled: boolean;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
createdBy: string;
|
||||||
|
updatedBy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProviderI {
|
||||||
|
id: string;
|
||||||
|
modelName: string;
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
baseUrl: string;
|
||||||
|
provider: string;
|
||||||
|
apiKey: string;
|
||||||
|
type: string;
|
||||||
|
isEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function EnvironmentAccess() {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [showModelDialog, setShowModelDialog] = useState(false);
|
||||||
|
const [isEditMode, setIsEditMode] = useState(false);
|
||||||
|
const [newModel, setNewModel] = useState({
|
||||||
|
name: "",
|
||||||
|
provider: "openai",
|
||||||
|
model: "",
|
||||||
|
apiKey: "",
|
||||||
|
endpoint: "",
|
||||||
|
});
|
||||||
|
const [typeOptions] = useState([
|
||||||
|
{ value: "CHAT", label: "CHAT" },
|
||||||
|
{ value: "EMBEDDING", label: "EMBEDDING" },
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
loading,
|
||||||
|
tableData,
|
||||||
|
pagination,
|
||||||
|
searchParams,
|
||||||
|
setSearchParams,
|
||||||
|
fetchData,
|
||||||
|
handleFiltersChange,
|
||||||
|
} = useFetchData(queryModelListUsingGet);
|
||||||
|
|
||||||
|
const handleAddModel = async () => {
|
||||||
|
const formValues = await form.validateFields();
|
||||||
|
const fn = isEditMode
|
||||||
|
? () => updateModelByIdUsingPut(newModel.id, formValues)
|
||||||
|
: () => createModelUsingPost(formValues);
|
||||||
|
await fn();
|
||||||
|
setShowModelDialog(false);
|
||||||
|
fetchData();
|
||||||
|
message.success("模型添加成功");
|
||||||
|
};
|
||||||
|
const [providerOptions, setProviderOptions] = useState<ProviderI[]>([]);
|
||||||
|
|
||||||
|
const fetchProviderOptions = async () => {
|
||||||
|
const { data } = await queryModelProvidersUsingGet();
|
||||||
|
setProviderOptions(
|
||||||
|
data.map((provider: ProviderI) => ({
|
||||||
|
...provider,
|
||||||
|
value: provider.provider,
|
||||||
|
label: provider.provider,
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const generateApiKey = () => {
|
||||||
|
const chars =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
let result = "sk-";
|
||||||
|
for (let i = 0; i < 48; i++) {
|
||||||
|
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteModel = async (modelId: string) => {
|
||||||
|
await deleteModelByIdUsingDelete(modelId);
|
||||||
|
fetchData();
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchProviderOptions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "模型名称",
|
||||||
|
dataIndex: "modelName",
|
||||||
|
key: "modelName",
|
||||||
|
fixed: "left",
|
||||||
|
width: 200,
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "创建时间",
|
||||||
|
dataIndex: "createdAt",
|
||||||
|
key: "createdAt",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "模型提供商",
|
||||||
|
dataIndex: "provider",
|
||||||
|
key: "provider",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "模型类型",
|
||||||
|
dataIndex: "type",
|
||||||
|
key: "type",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "更新时间",
|
||||||
|
dataIndex: "updatedAt",
|
||||||
|
key: "updatedAt",
|
||||||
|
ellipsis: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "操作",
|
||||||
|
key: "action",
|
||||||
|
fixed: "right" as const,
|
||||||
|
ellipsis: true,
|
||||||
|
render: (_: any, record: ModelI) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: "edit",
|
||||||
|
label: "编辑",
|
||||||
|
icon: <EditOutlined />,
|
||||||
|
onClick: () => {
|
||||||
|
setIsEditMode(true);
|
||||||
|
setNewModel(record);
|
||||||
|
form.setFieldsValue(record);
|
||||||
|
setShowModelDialog(true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "delete",
|
||||||
|
label: "删除",
|
||||||
|
danger: true,
|
||||||
|
icon: <DeleteOutlined />,
|
||||||
|
confirm: {
|
||||||
|
title: "确定要删除该任务吗?此操作不可撤销。",
|
||||||
|
okText: "删除",
|
||||||
|
cancelText: "取消",
|
||||||
|
okType: "danger",
|
||||||
|
},
|
||||||
|
onClick: () => handleDeleteModel(record.id),
|
||||||
|
},
|
||||||
|
].map((op) => {
|
||||||
|
const button = (
|
||||||
|
<Tooltip key={op.key} title={op.label}>
|
||||||
|
<Button
|
||||||
|
type="text"
|
||||||
|
icon={op.icon}
|
||||||
|
danger={op?.danger}
|
||||||
|
onClick={() => op.onClick(record)}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
if (op.confirm) {
|
||||||
|
return (
|
||||||
|
<Popconfirm
|
||||||
|
key={op.key}
|
||||||
|
title={op.confirm.title}
|
||||||
|
okText={op.confirm.okText}
|
||||||
|
cancelText={op.confirm.cancelText}
|
||||||
|
okType={op.danger ? "danger" : "primary"}
|
||||||
|
onConfirm={() => op.onClick(record)}
|
||||||
|
>
|
||||||
|
<Tooltip key={op.key} title={op.label}>
|
||||||
|
<Button type="text" icon={op.icon} danger={op?.danger} />
|
||||||
|
</Tooltip>
|
||||||
|
</Popconfirm>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return button;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flex items-top justify-between">
|
||||||
|
<h2 className="text-lg font-medium mb-4">模型接入</h2>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
onClick={() => {
|
||||||
|
setIsEditMode(false);
|
||||||
|
form.resetFields();
|
||||||
|
setNewModel({
|
||||||
|
name: "",
|
||||||
|
provider: "",
|
||||||
|
model: "",
|
||||||
|
apiKey: "",
|
||||||
|
endpoint: "",
|
||||||
|
});
|
||||||
|
setShowModelDialog(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
添加模型
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<SearchControls
|
||||||
|
searchTerm={searchParams.keyword}
|
||||||
|
onSearchChange={(newSearchTerm) =>
|
||||||
|
setSearchParams((prev) => ({
|
||||||
|
...prev,
|
||||||
|
keyword: newSearchTerm,
|
||||||
|
current: 1,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
searchPlaceholder="搜索模型描述..."
|
||||||
|
filters={[
|
||||||
|
{
|
||||||
|
key: "provider",
|
||||||
|
label: "模型提供商",
|
||||||
|
options: [{ value: "all", label: "全部" }, ...providerOptions],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "type",
|
||||||
|
label: "模型类型",
|
||||||
|
options: [{ value: "all", label: "全部" }, ...typeOptions],
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onFiltersChange={handleFiltersChange}
|
||||||
|
showViewToggle={false}
|
||||||
|
onReload={fetchData}
|
||||||
|
onClearFilters={() =>
|
||||||
|
setSearchParams((prev) => ({
|
||||||
|
...prev,
|
||||||
|
filters: {},
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
className="mb-4"
|
||||||
|
/>
|
||||||
|
<Card>
|
||||||
|
<Table
|
||||||
|
rowKey="id"
|
||||||
|
columns={columns}
|
||||||
|
dataSource={tableData}
|
||||||
|
loading={loading}
|
||||||
|
pagination={pagination}
|
||||||
|
scroll={{ x: "max-content", y: "calc(100vh - 26rem)" }}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
<Modal
|
||||||
|
open={showModelDialog}
|
||||||
|
onCancel={() => setShowModelDialog(false)}
|
||||||
|
title={isEditMode ? "编辑模型" : "添加模型"}
|
||||||
|
footer={[
|
||||||
|
<Button key="cancel" onClick={() => setShowModelDialog(false)}>
|
||||||
|
取消
|
||||||
|
</Button>,
|
||||||
|
<Button key="ok" type="primary" onClick={handleAddModel}>
|
||||||
|
确定
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
onValuesChange={(changedValues) => {
|
||||||
|
setNewModel({ ...newModel, ...changedValues });
|
||||||
|
}}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="modelName"
|
||||||
|
label="模型名称"
|
||||||
|
required
|
||||||
|
rules={[{ required: true, message: "请输入模型名称" }]}
|
||||||
|
>
|
||||||
|
<Input placeholder="输入模型名称" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="provider"
|
||||||
|
label="服务提供商"
|
||||||
|
required
|
||||||
|
rules={[{ required: true, message: "请选择服务提供商" }]}
|
||||||
|
>
|
||||||
|
<Select
|
||||||
|
placeholder="选择服务提供商"
|
||||||
|
options={providerOptions}
|
||||||
|
onChange={(value) => {
|
||||||
|
const selectedProvider = providerOptions.find(
|
||||||
|
(p) => p.value === value
|
||||||
|
);
|
||||||
|
form.setFieldsValue({ baseUrl: selectedProvider?.baseUrl });
|
||||||
|
}}
|
||||||
|
></Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="baseUrl"
|
||||||
|
label="接口地址"
|
||||||
|
required
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: "请输入接口地址" },
|
||||||
|
{
|
||||||
|
pattern: /^https?:\/\/.+/,
|
||||||
|
message: "请输入有效的URL地址,必须以http://或https://开头",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder="输入接口地址,如:https://api.openai.com" />
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="apiKey"
|
||||||
|
label="API密钥"
|
||||||
|
required
|
||||||
|
rules={[{ required: true, message: "请输入API密钥" }]}
|
||||||
|
>
|
||||||
|
<Input
|
||||||
|
placeholder="输入或生成API密钥"
|
||||||
|
addonAfter={
|
||||||
|
<ReloadOutlined
|
||||||
|
onClick={() => {
|
||||||
|
form.setFieldsValue({ apiKey: generateApiKey() });
|
||||||
|
setNewModel({ ...newModel, apiKey: generateApiKey() });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="type"
|
||||||
|
label="模型类型"
|
||||||
|
required
|
||||||
|
rules={[{ required: true, message: "请选择模型类型" }]}
|
||||||
|
>
|
||||||
|
<Select options={typeOptions} placeholder="选择模型类型"></Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
import { useState } from "react";
|
|
||||||
import { Tabs } from "antd";
|
|
||||||
import {
|
|
||||||
SettingOutlined,
|
|
||||||
DatabaseOutlined,
|
|
||||||
ApiOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import DevelopmentInProgress from "@/components/DevelopmentInProgress";
|
|
||||||
import WebhookConfig from "./components/WebhookConfig";
|
|
||||||
import EnvironmentAccess from "./components/EnvironmentAccess";
|
|
||||||
import SystemConfig from "./components/SystemConfig";
|
|
||||||
|
|
||||||
export default function SettingsPage() {
|
|
||||||
return <DevelopmentInProgress />;
|
|
||||||
const [activeTab, setActiveTab] = useState("system");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-6">
|
|
||||||
{/* Header */}
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div>
|
|
||||||
<h1 className="text-xl font-bold text-gray-900">系统设置</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Settings Tabs */}
|
|
||||||
<Tabs
|
|
||||||
activeKey={activeTab}
|
|
||||||
onChange={setActiveTab}
|
|
||||||
className="space-y-6"
|
|
||||||
items={[
|
|
||||||
{
|
|
||||||
key: "system",
|
|
||||||
label: (
|
|
||||||
<span>
|
|
||||||
<SettingOutlined className="mr-1" />
|
|
||||||
系统设置
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
children: <SystemConfig />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "environment",
|
|
||||||
label: (
|
|
||||||
<span>
|
|
||||||
<DatabaseOutlined className="mr-1" />
|
|
||||||
环境接入
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
children: <EnvironmentAccess />,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "webhook",
|
|
||||||
label: (
|
|
||||||
<span>
|
|
||||||
<ApiOutlined className="mr-1" />
|
|
||||||
Webhook
|
|
||||||
</span>
|
|
||||||
),
|
|
||||||
children: <WebhookConfig />,
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
60
frontend/src/pages/SettingsPage/SettingsPage.tsx
Normal file
60
frontend/src/pages/SettingsPage/SettingsPage.tsx
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { Tabs } from "antd";
|
||||||
|
import { SettingOutlined, ApiOutlined } from "@ant-design/icons";
|
||||||
|
import WebhookConfig from "./WebhookConfig";
|
||||||
|
import ModelAccess from "./ModelAccess";
|
||||||
|
import SystemConfig from "./SystemConfig";
|
||||||
|
import { Component } from "lucide-react";
|
||||||
|
|
||||||
|
export default function SettingsPage() {
|
||||||
|
const [activeTab, setActiveTab] = useState("modelAccess");
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="h-full flex flex-col gap-4">
|
||||||
|
{/* Header */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-xl font-bold text-gray-900">系统设置</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Settings Tabs */}
|
||||||
|
<Tabs
|
||||||
|
activeKey={activeTab}
|
||||||
|
onChange={setActiveTab}
|
||||||
|
items={[
|
||||||
|
// {
|
||||||
|
// key: "system",
|
||||||
|
// label: (
|
||||||
|
// <span>
|
||||||
|
// <SettingOutlined className="mr-1" />
|
||||||
|
// 系统设置
|
||||||
|
// </span>
|
||||||
|
// ),
|
||||||
|
// children: <SystemConfig />,
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: "modelAccess",
|
||||||
|
label: (
|
||||||
|
<span className="flex items-center">
|
||||||
|
<Component className="w-4 h-4 mr-1" />
|
||||||
|
模型接入
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
children: <ModelAccess key="modelAccess" />,
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// key: "webhook",
|
||||||
|
// label: (
|
||||||
|
// <span>
|
||||||
|
// <ApiOutlined className="mr-1" />
|
||||||
|
// Webhook
|
||||||
|
// </span>
|
||||||
|
// ),
|
||||||
|
// children: <WebhookConfig />,
|
||||||
|
// },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ export default function SystemConfig() {
|
|||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
// System Settings State
|
// System Settings State
|
||||||
const [systemConfig, setSystemConfig] = useState({
|
const [systemConfig, setSystemConfig] = useState({
|
||||||
siteName: "ML Dataset Tool",
|
siteName: "DataMate",
|
||||||
maxFileSize: "100",
|
maxFileSize: "100",
|
||||||
autoBackup: true,
|
autoBackup: true,
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
@@ -1,365 +0,0 @@
|
|||||||
import { Card, Button, Form, Input, Modal, Select, Badge } from "antd";
|
|
||||||
import {
|
|
||||||
EditOutlined,
|
|
||||||
DeleteOutlined,
|
|
||||||
ReloadOutlined,
|
|
||||||
ExperimentOutlined,
|
|
||||||
EyeOutlined,
|
|
||||||
EyeInvisibleOutlined,
|
|
||||||
CopyOutlined,
|
|
||||||
} from "@ant-design/icons";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
interface VectorDBConfig {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
type: "pinecone" | "weaviate" | "qdrant" | "milvus" | "chroma";
|
|
||||||
url: string;
|
|
||||||
apiKey: string;
|
|
||||||
dimension: number;
|
|
||||||
metric: string;
|
|
||||||
status: "connected" | "disconnected" | "error";
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ModelConfig {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
provider: "openai" | "anthropic" | "google" | "azure" | "local";
|
|
||||||
model: string;
|
|
||||||
apiKey: string;
|
|
||||||
endpoint?: string;
|
|
||||||
status: "active" | "inactive";
|
|
||||||
usage: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function EnvironmentAccess() {
|
|
||||||
const [form] = Form.useForm();
|
|
||||||
const [showApiKey, setShowApiKey] = useState<{ [key: string]: boolean }>({});
|
|
||||||
const [showVectorDBDialog, setShowVectorDBDialog] = useState(false);
|
|
||||||
const [showModelDialog, setShowModelDialog] = useState(false);
|
|
||||||
|
|
||||||
const [vectorDBs, setVectorDBs] = useState<VectorDBConfig[]>([
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "Pinecone Production",
|
|
||||||
type: "pinecone",
|
|
||||||
url: "https://your-index.svc.us-east1-gcp.pinecone.io",
|
|
||||||
apiKey: "pc-****-****-****",
|
|
||||||
dimension: 1536,
|
|
||||||
metric: "cosine",
|
|
||||||
status: "connected",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Weaviate Local",
|
|
||||||
type: "weaviate",
|
|
||||||
url: "http://localhost:8080",
|
|
||||||
apiKey: "",
|
|
||||||
dimension: 768,
|
|
||||||
metric: "cosine",
|
|
||||||
status: "disconnected",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
const [providerOptions] = useState([
|
|
||||||
{ value: "openai", label: "OpenAI" },
|
|
||||||
{ value: "anthropic", label: "Anthropic" },
|
|
||||||
{ value: "google", label: "Google" },
|
|
||||||
{ value: "azure", label: "Azure" },
|
|
||||||
{ value: "local", label: "本地部署" },
|
|
||||||
]);
|
|
||||||
const [models, setModels] = useState<ModelConfig[]>([
|
|
||||||
{
|
|
||||||
id: "1",
|
|
||||||
name: "GPT-4 Turbo",
|
|
||||||
provider: "openai",
|
|
||||||
model: "gpt-4-turbo-preview",
|
|
||||||
apiKey: "sk-****-****-****",
|
|
||||||
status: "active",
|
|
||||||
usage: 85,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "2",
|
|
||||||
name: "Claude 3 Sonnet",
|
|
||||||
provider: "anthropic",
|
|
||||||
model: "claude-3-sonnet-20240229",
|
|
||||||
apiKey: "sk-ant-****-****",
|
|
||||||
status: "active",
|
|
||||||
usage: 42,
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [dbOptions] = useState([
|
|
||||||
{ value: "pinecone", label: "Pinecone" },
|
|
||||||
{ value: "weaviate", label: "Weaviate" },
|
|
||||||
{ value: "qdrant", label: "Qdrant" },
|
|
||||||
{ value: "milvus", label: "Milvus" },
|
|
||||||
{ value: "chroma", label: "Chroma" },
|
|
||||||
]);
|
|
||||||
const [metricOptions] = useState([
|
|
||||||
{ value: "cosine", label: "Cosine" },
|
|
||||||
{ value: "euclidean", label: "Euclidean" },
|
|
||||||
{ value: "dotproduct", label: "Dot Product" },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const [newVectorDB, setNewVectorDB] = useState({
|
|
||||||
name: "",
|
|
||||||
type: "pinecone",
|
|
||||||
url: "",
|
|
||||||
apiKey: "",
|
|
||||||
dimension: 1536,
|
|
||||||
metric: "cosine",
|
|
||||||
});
|
|
||||||
const [newModel, setNewModel] = useState({
|
|
||||||
name: "",
|
|
||||||
provider: "openai",
|
|
||||||
model: "",
|
|
||||||
apiKey: "",
|
|
||||||
endpoint: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleAddVectorDB = () => {
|
|
||||||
setNewVectorDB({
|
|
||||||
name: "",
|
|
||||||
type: "pinecone",
|
|
||||||
url: "",
|
|
||||||
apiKey: "",
|
|
||||||
dimension: 1536,
|
|
||||||
metric: "cosine",
|
|
||||||
});
|
|
||||||
setShowVectorDBDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleAddModel = () => {
|
|
||||||
setNewModel({
|
|
||||||
name: "",
|
|
||||||
provider: "openai",
|
|
||||||
model: "",
|
|
||||||
apiKey: generateApiKey(),
|
|
||||||
endpoint: "",
|
|
||||||
});
|
|
||||||
setShowModelDialog(true);
|
|
||||||
};
|
|
||||||
const toggleApiKeyVisibility = (id: string) => {
|
|
||||||
setShowApiKey((prev) => ({ ...prev, [id]: !prev[id] }));
|
|
||||||
};
|
|
||||||
|
|
||||||
const generateApiKey = () => {
|
|
||||||
const chars =
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
||||||
let result = "sk-";
|
|
||||||
for (let i = 0; i < 48; i++) {
|
|
||||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
||||||
<Card>
|
|
||||||
<div className="flex items-top justify-between">
|
|
||||||
<h2 className="text-base font-medium mb-4">向量数据库</h2>
|
|
||||||
<Button type="link" onClick={handleAddVectorDB}>
|
|
||||||
添加向量库
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
{vectorDBs.map((db) => (
|
|
||||||
<Card key={db.id} className="border rounded-lg p-4 space-y-2">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="font-medium">{db.name}</span>
|
|
||||||
<Badge
|
|
||||||
status={
|
|
||||||
db.status === "connected"
|
|
||||||
? "success"
|
|
||||||
: db.status === "error"
|
|
||||||
? "error"
|
|
||||||
: "default"
|
|
||||||
}
|
|
||||||
text={
|
|
||||||
db.status === "connected"
|
|
||||||
? "已连接"
|
|
||||||
: db.status === "error"
|
|
||||||
? "异常"
|
|
||||||
: "未连接"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Button icon={<ExperimentOutlined />} size="small" />
|
|
||||||
<Button icon={<EditOutlined />} size="small" />
|
|
||||||
<Button icon={<DeleteOutlined />} size="small" danger />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-600 space-y-1">
|
|
||||||
<p>类型: {db.type}</p>
|
|
||||||
<p>地址: {db.url}</p>
|
|
||||||
<p>
|
|
||||||
维度: {db.dimension} | 距离度量: {db.metric}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
<Card>
|
|
||||||
<div className="flex items-top justify-between">
|
|
||||||
<h2 className="text-base font-medium mb-4">模型接入</h2>
|
|
||||||
<Button type="link" onClick={handleAddModel}>
|
|
||||||
添加模型
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-3">
|
|
||||||
{models.map((model) => (
|
|
||||||
<Card key={model.id} className="border rounded-lg p-4 space-y-2">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span className="font-medium">{model.name}</span>
|
|
||||||
<Badge
|
|
||||||
status={model.status === "active" ? "success" : "default"}
|
|
||||||
text={model.status === "active" ? "启用" : "禁用"}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Button
|
|
||||||
icon={
|
|
||||||
showApiKey[model.id] ? (
|
|
||||||
<EyeInvisibleOutlined />
|
|
||||||
) : (
|
|
||||||
<EyeOutlined />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
size="small"
|
|
||||||
onClick={() => toggleApiKeyVisibility(model.id)}
|
|
||||||
/>
|
|
||||||
<Button icon={<ReloadOutlined />} size="small" />
|
|
||||||
<Button icon={<EditOutlined />} size="small" />
|
|
||||||
<Button icon={<DeleteOutlined />} size="small" danger />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="text-sm text-gray-600 space-y-1">
|
|
||||||
<p>提供商: {model.provider}</p>
|
|
||||||
<p>模型: {model.model}</p>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span>API Key:</span>
|
|
||||||
<code className="bg-gray-100 px-2 py-1 rounded text-xs">
|
|
||||||
{showApiKey[model.id] ? model.apiKey : "sk-****-****-****"}
|
|
||||||
</code>
|
|
||||||
<Button
|
|
||||||
icon={<CopyOutlined />}
|
|
||||||
size="small"
|
|
||||||
onClick={() => navigator.clipboard.writeText(model.apiKey)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<span>使用率:</span>
|
|
||||||
<div className="flex-1 bg-gray-200 rounded-full h-2">
|
|
||||||
<div
|
|
||||||
className="bg-blue-500 h-2 rounded-full"
|
|
||||||
style={{ width: `${model.usage}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<span className="text-xs">{model.usage}%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</Card>
|
|
||||||
{/* VectorDB Modal */}
|
|
||||||
<Modal
|
|
||||||
open={showVectorDBDialog}
|
|
||||||
onCancel={() => setShowVectorDBDialog(false)}
|
|
||||||
title="添加向量数据库"
|
|
||||||
footer={[
|
|
||||||
<Button key="cancel" onClick={() => setShowVectorDBDialog(false)}>
|
|
||||||
取消
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key="ok"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => setShowVectorDBDialog(false)}
|
|
||||||
>
|
|
||||||
添加数据库
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Form layout="vertical">
|
|
||||||
<Form.Item name="name" label="数据库名称">
|
|
||||||
<Input placeholder="输入数据库名称" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="type" label="数据库类型">
|
|
||||||
<Select options={dbOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="url" label="连接地址">
|
|
||||||
<Input placeholder="https://your-index.svc.region.pinecone.io" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="apiKey" label="API Key">
|
|
||||||
<Input type="password" placeholder="输入API密钥" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="dimension" label="向量维度">
|
|
||||||
<Input type="number" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="metric" label="距离度量">
|
|
||||||
<Select options={metricOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
{/* Model Modal */}
|
|
||||||
<Modal
|
|
||||||
open={showModelDialog}
|
|
||||||
onCancel={() => setShowModelDialog(false)}
|
|
||||||
title="添加AI模型"
|
|
||||||
footer={[
|
|
||||||
<Button key="cancel" onClick={() => setShowModelDialog(false)}>
|
|
||||||
取消
|
|
||||||
</Button>,
|
|
||||||
<Button
|
|
||||||
key="ok"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => setShowModelDialog(false)}
|
|
||||||
>
|
|
||||||
添加模型
|
|
||||||
</Button>,
|
|
||||||
]}
|
|
||||||
>
|
|
||||||
<Form
|
|
||||||
form={form}
|
|
||||||
onValuesChange={(changedValues) => {
|
|
||||||
setNewModel({ ...newModel, ...changedValues });
|
|
||||||
}}
|
|
||||||
layout="vertical"
|
|
||||||
>
|
|
||||||
<Form.Item name="name" label="模型名称">
|
|
||||||
<Input placeholder="输入模型名称" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="provider" label="服务提供商">
|
|
||||||
<Select options={providerOptions}></Select>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="model" label="模型标识">
|
|
||||||
<Input placeholder="gpt-4-turbo-preview" />
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item name="apiKey" label="API Key">
|
|
||||||
<Input
|
|
||||||
placeholder="输入或生成API密钥"
|
|
||||||
addonAfter={
|
|
||||||
<ReloadOutlined
|
|
||||||
onClick={() => {
|
|
||||||
form.setFieldsValue({ apiKey: generateApiKey() });
|
|
||||||
setNewModel({ ...newModel, apiKey: generateApiKey() });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
{newModel.provider === "local" && (
|
|
||||||
<Form.Item name="endpoint" label="自定义端点">
|
|
||||||
<Input placeholder="http://localhost:8000/v1" />
|
|
||||||
</Form.Item>
|
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
30
frontend/src/pages/SettingsPage/settings.apis.ts
Normal file
30
frontend/src/pages/SettingsPage/settings.apis.ts
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
import { get, post, put, del } from "@/utils/request";
|
||||||
|
|
||||||
|
// 模型相关接口
|
||||||
|
export function queryModelProvidersUsingGet(params?: any) {
|
||||||
|
return get("/api/models/providers", params);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryModelListUsingGet(data: any) {
|
||||||
|
return get("/api/models/list", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function queryModelDetailByIdUsingGet(id: string | number) {
|
||||||
|
return get(`/api/models/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateModelByIdUsingPut(
|
||||||
|
id: string | number,
|
||||||
|
data: any
|
||||||
|
) {
|
||||||
|
return put(`/api/models/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createModelUsingPost(data: any) {
|
||||||
|
return post("/api/models/create", data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteModelByIdUsingDelete(id: string | number) {
|
||||||
|
return del(`/api/models/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,7 +2,7 @@ import { createBrowserRouter } from "react-router";
|
|||||||
import Home from "../pages/Home/Home";
|
import Home from "../pages/Home/Home";
|
||||||
import MainLayout from "../pages/Layout/MainLayout";
|
import MainLayout from "../pages/Layout/MainLayout";
|
||||||
|
|
||||||
import DataCollection from "@/pages/DataCollection/Home/DataCollection";
|
import DataCollection from "@/pages/DataCollection/Home/DataCollectionPage";
|
||||||
import CollectionTaskCreate from "@/pages/DataCollection/Create/CreateTask";
|
import CollectionTaskCreate from "@/pages/DataCollection/Create/CreateTask";
|
||||||
|
|
||||||
import DatasetManagement from "@/pages/DataManagement/Home/DataManagement";
|
import DatasetManagement from "@/pages/DataManagement/Home/DataManagement";
|
||||||
@@ -44,7 +44,7 @@ import CreateRatioTask from "@/pages/RatioTask/CreateRatioTask";
|
|||||||
import OrchestrationPage from "@/pages/Orchestration/Orchestration";
|
import OrchestrationPage from "@/pages/Orchestration/Orchestration";
|
||||||
import WorkflowEditor from "@/pages/Orchestration/WorkflowEditor";
|
import WorkflowEditor from "@/pages/Orchestration/WorkflowEditor";
|
||||||
import AgentPage from "@/pages/Agent/Agent";
|
import AgentPage from "@/pages/Agent/Agent";
|
||||||
import SettingsPage from "@/pages/SettingsPage/Settings";
|
import SettingsPage from "@/pages/SettingsPage/SettingsPage";
|
||||||
import { withErrorBoundary } from "@/components/ErrorBoundary";
|
import { withErrorBoundary } from "@/components/ErrorBoundary";
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
|
|||||||
Reference in New Issue
Block a user