diff --git a/frontend/index.html b/frontend/index.html index 9370cde..278d49b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - ML Dataset Tool + DataMate
diff --git a/frontend/src/mock/mock-apis.cjs b/frontend/src/mock/mock-apis.cjs index ae601e6..dba93f1 100644 --- a/frontend/src/mock/mock-apis.cjs +++ b/frontend/src/mock/mock-apis.cjs @@ -2,10 +2,11 @@ const { addMockPrefix } = require("./mock-core/util.cjs"); const MockAPI = { // 数据归集接口 - queryTasksUsingPost: "/data-collection/tasks", // 获取数据源任务列表 + queryTasksUsingGet: "/data-collection/tasks", // 获取数据源任务列表 createTaskUsingPost: "/data-collection/tasks/create", // 创建数据源任务 queryTaskByIdUsingGet: "/data-collection/tasks/:id", // 根据ID获取数据源任务详情 updateTaskByIdUsingPut: "/data-collection/tasks/:id", // 更新数据源任务 + queryDataXTemplatesUsingGet: "/data-collection/templates", // 获取DataX数据源模板列表 deleteTaskByIdUsingDelete: "/data-collection/tasks/:id", // 删除数据源任务 executeTaskByIdUsingPost: "/data-collection/tasks/:id/execute", // 执行数据源任务 stopTaskByIdUsingPost: "/data-collection/tasks/:id/stop", // 停止数据源任务 @@ -91,7 +92,6 @@ const MockAPI = { deleteInstructionTemplateByIdUsingDelete: "/synthesis/templates/:templateId", // 删除指令模板 instructionTuningUsingPost: "/synthesis/instruction-tuning", // 指令微调 cotDistillationUsingPost: "/synthesis/cot-distillation", // Cot蒸馏 - queryOperatorsUsingPost: "/synthesis/operators", // 获取操作列表 // 数据评测接口 queryEvaluationTasksUsingPost: "/evaluation/tasks", // 获取评测任务列表 @@ -144,6 +144,14 @@ const MockAPI = { deleteOperatorByIdUsingDelete: "/operators/:operatorId", // 删除算子 publishOperatorUsingPost: "/operators/:operatorId/publish", // 发布算子 unpublishOperatorUsingPost: "/operators/:operatorId/unpublish", // 下架算子 + + // 设置接口 + queryModelsUsingGet: "/models/list", // 获取模型列表 + queryProvidersUsingGet: "/models/providers", // 获取模型提供商列表 + createModelUsingPost: "/models/create", // 创建模型 + updateModelUsingPut: "/models/:id", // 更新模型 + deleteModelUsingDelete: "/models/:id", // 删除模型 + }; module.exports = addMockPrefix("/api", MockAPI); diff --git a/frontend/src/mock/mock-seed/data-management.cjs b/frontend/src/mock/mock-seed/data-management.cjs index 098439b..4a66765 100644 --- a/frontend/src/mock/mock-seed/data-management.cjs +++ b/frontend/src/mock/mock-seed/data-management.cjs @@ -16,8 +16,8 @@ function datasetItem() { return { id: Mock.Random.guid().replace(/[^a-zA-Z0-9]/g, ""), name: Mock.Random.ctitle(5, 20), - type: Mock.Random.pick(["TEXT", "IMAGE", "AUDIO", "VIDEO"]), - status: Mock.Random.pick(["ACTIVE", "INACTIVE", "PROCESSING"]), + datasetType: Mock.Random.pick(["TEXT", "IMAGE", "AUDIO", "VIDEO"]), + status: Mock.Random.pick(["DRAFT","ACTIVE", "INACTIVE", "PROCESSING"]), tags: Mock.Random.shuffle(tagList).slice(0, Mock.Random.integer(1, 3)), totalSize: Mock.Random.integer(1024, 1024 * 1024 * 1024), // in bytes description: Mock.Random.cparagraph(1, 3), @@ -164,7 +164,7 @@ module.exports = function (router) { console.log("filter type:", type); filteredDatasets = filteredDatasets.filter( - (dataset) => dataset.type === type + (dataset) => dataset.datasetType === type ); } if (status) { diff --git a/frontend/src/mock/mock-seed/settings.cjs b/frontend/src/mock/mock-seed/settings.cjs new file mode 100644 index 0000000..886a0b3 --- /dev/null +++ b/frontend/src/mock/mock-seed/settings.cjs @@ -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, + }); + }); +}; diff --git a/frontend/src/pages/DataManagement/Home/DataManagement.tsx b/frontend/src/pages/DataManagement/Home/DataManagement.tsx index 7b88580..f94b3b5 100644 --- a/frontend/src/pages/DataManagement/Home/DataManagement.tsx +++ b/frontend/src/pages/DataManagement/Home/DataManagement.tsx @@ -51,11 +51,11 @@ export default function DatasetManagementPage() { }, { title: "文件总数", - value: data?.totalFiles || "0 MB", + value: data?.totalFiles || 0, }, { title: "总大小", - value: formatBytes(data?.totalSize) || 0, + value: formatBytes(data?.totalSize) || '0 B', }, ], count: [ diff --git a/frontend/src/pages/Home/Home.tsx b/frontend/src/pages/Home/Home.tsx index 0858766..ee15b52 100644 --- a/frontend/src/pages/Home/Home.tsx +++ b/frontend/src/pages/Home/Home.tsx @@ -29,8 +29,7 @@ export default function WelcomePage() { AI数据集

- 从数据管理到知识生成,一站式解决机器学习数据准备的所有需求。 - 支持对话式操作、智能编排、数据合成、智能标注、全面评估和RAG知识库构建。 + 从数据管理到知识生成,一站式解决企业AI数据处理的场景问题。

{ + 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([]); + + 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: , + onClick: () => { + setIsEditMode(true); + setNewModel(record); + form.setFieldsValue(record); + setShowModelDialog(true); + }, + }, + { + key: "delete", + label: "删除", + danger: true, + icon: , + confirm: { + title: "确定要删除该任务吗?此操作不可撤销。", + okText: "删除", + cancelText: "取消", + okType: "danger", + }, + onClick: () => handleDeleteModel(record.id), + }, + ].map((op) => { + const button = ( + + +
+ + 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" + /> + + + + setShowModelDialog(false)} + title={isEditMode ? "编辑模型" : "添加模型"} + footer={[ + , + , + ]} + > +
{ + setNewModel({ ...newModel, ...changedValues }); + }} + layout="vertical" + > + + + + + + + + + + + { + form.setFieldsValue({ apiKey: generateApiKey() }); + setNewModel({ ...newModel, apiKey: generateApiKey() }); + }} + /> + } + /> + + + + + +
+ + ); +} diff --git a/frontend/src/pages/SettingsPage/Settings.tsx b/frontend/src/pages/SettingsPage/Settings.tsx deleted file mode 100644 index f186018..0000000 --- a/frontend/src/pages/SettingsPage/Settings.tsx +++ /dev/null @@ -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 ; - const [activeTab, setActiveTab] = useState("system"); - - return ( -
- {/* Header */} -
-
-

系统设置

-
-
- - {/* Settings Tabs */} - - - 系统设置 - - ), - children: , - }, - { - key: "environment", - label: ( - - - 环境接入 - - ), - children: , - }, - { - key: "webhook", - label: ( - - - Webhook - - ), - children: , - }, - ]} - /> -
- ); -} diff --git a/frontend/src/pages/SettingsPage/SettingsPage.tsx b/frontend/src/pages/SettingsPage/SettingsPage.tsx new file mode 100644 index 0000000..1424d0f --- /dev/null +++ b/frontend/src/pages/SettingsPage/SettingsPage.tsx @@ -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 ( +
+ {/* Header */} +
+
+

系统设置

+
+
+ + {/* Settings Tabs */} + + // + // 系统设置 + // + // ), + // children: , + // }, + { + key: "modelAccess", + label: ( + + + 模型接入 + + ), + children: , + }, + // { + // key: "webhook", + // label: ( + // + // + // Webhook + // + // ), + // children: , + // }, + ]} + /> +
+ ); +} diff --git a/frontend/src/pages/SettingsPage/components/SystemConfig.tsx b/frontend/src/pages/SettingsPage/SystemConfig.tsx similarity index 98% rename from frontend/src/pages/SettingsPage/components/SystemConfig.tsx rename to frontend/src/pages/SettingsPage/SystemConfig.tsx index a4afc1e..6ca7277 100644 --- a/frontend/src/pages/SettingsPage/components/SystemConfig.tsx +++ b/frontend/src/pages/SettingsPage/SystemConfig.tsx @@ -5,7 +5,7 @@ export default function SystemConfig() { const { message } = App.useApp(); // System Settings State const [systemConfig, setSystemConfig] = useState({ - siteName: "ML Dataset Tool", + siteName: "DataMate", maxFileSize: "100", autoBackup: true, logLevel: "info", diff --git a/frontend/src/pages/SettingsPage/components/WebhookConfig.tsx b/frontend/src/pages/SettingsPage/WebhookConfig.tsx similarity index 100% rename from frontend/src/pages/SettingsPage/components/WebhookConfig.tsx rename to frontend/src/pages/SettingsPage/WebhookConfig.tsx diff --git a/frontend/src/pages/SettingsPage/components/EnvironmentAccess.tsx b/frontend/src/pages/SettingsPage/components/EnvironmentAccess.tsx deleted file mode 100644 index f02a5bb..0000000 --- a/frontend/src/pages/SettingsPage/components/EnvironmentAccess.tsx +++ /dev/null @@ -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([ - { - 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([ - { - 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 ( -
- -
-

向量数据库

- -
-
- {vectorDBs.map((db) => ( - -
-
- {db.name} - -
-
-
-
-
-

类型: {db.type}

-

地址: {db.url}

-

- 维度: {db.dimension} | 距离度量: {db.metric} -

-
-
- ))} -
-
- -
-

模型接入

- -
-
- {models.map((model) => ( - -
-
- {model.name} - -
-
-
-
-
-

提供商: {model.provider}

-

模型: {model.model}

-
- API Key: - - {showApiKey[model.id] ? model.apiKey : "sk-****-****-****"} - -
-
- 使用率: -
-
-
- {model.usage}% -
-
- - ))} -
-
- {/* VectorDB Modal */} - setShowVectorDBDialog(false)} - title="添加向量数据库" - footer={[ - , - , - ]} - > -
- - - - - - - - - - - - - - - - - - - -
- - {/* Model Modal */} - setShowModelDialog(false)} - title="添加AI模型" - footer={[ - , - , - ]} - > -
{ - setNewModel({ ...newModel, ...changedValues }); - }} - layout="vertical" - > - - - - - - - - - - - { - form.setFieldsValue({ apiKey: generateApiKey() }); - setNewModel({ ...newModel, apiKey: generateApiKey() }); - }} - /> - } - /> - - {newModel.provider === "local" && ( - - - - )} - -
-
- ); -} diff --git a/frontend/src/pages/SettingsPage/settings.apis.ts b/frontend/src/pages/SettingsPage/settings.apis.ts new file mode 100644 index 0000000..3efbc2d --- /dev/null +++ b/frontend/src/pages/SettingsPage/settings.apis.ts @@ -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}`); +} + diff --git a/frontend/src/routes/routes.ts b/frontend/src/routes/routes.ts index 763abf6..9e1adb9 100644 --- a/frontend/src/routes/routes.ts +++ b/frontend/src/routes/routes.ts @@ -2,7 +2,7 @@ import { createBrowserRouter } from "react-router"; import Home from "../pages/Home/Home"; 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 DatasetManagement from "@/pages/DataManagement/Home/DataManagement"; @@ -44,7 +44,7 @@ import CreateRatioTask from "@/pages/RatioTask/CreateRatioTask"; import OrchestrationPage from "@/pages/Orchestration/Orchestration"; import WorkflowEditor from "@/pages/Orchestration/WorkflowEditor"; import AgentPage from "@/pages/Agent/Agent"; -import SettingsPage from "@/pages/SettingsPage/Settings"; +import SettingsPage from "@/pages/SettingsPage/SettingsPage"; import { withErrorBoundary } from "@/components/ErrorBoundary"; const router = createBrowserRouter([