refactor: Add frontend code to DB-GPT (#912)

This commit is contained in:
katakuri
2023-12-11 00:05:42 +08:00
committed by GitHub
parent b8dc9cf11e
commit 43190ca333
189 changed files with 19179 additions and 16 deletions

View File

@@ -0,0 +1,85 @@
import React, { useState } from 'react';
import { IModelData } from '@/types/model';
import { useTranslation } from 'react-i18next';
import SentimentSatisfiedAltIcon from '@mui/icons-material/SentimentSatisfiedAlt';
import SentimentVeryDissatisfiedIcon from '@mui/icons-material/SentimentVeryDissatisfied';
import StopCircleIcon from '@mui/icons-material/StopCircle';
import { Tooltip, message } from 'antd';
import moment from 'moment';
import { apiInterceptors, stopModel } from '@/client/api';
import { renderModelIcon } from '../chat/header/model-selector';
interface Props {
info: IModelData;
}
function ModelCard({ info }: Props) {
const { t } = useTranslation();
const [loading, setLoading] = useState<boolean>(false);
async function stopTheModel(info: IModelData) {
if (loading) {
return;
}
setLoading(true);
const [, res] = await apiInterceptors(
stopModel({
host: info.host,
port: info.port,
model: info.model_name,
worker_type: info.model_type,
params: {},
}),
);
setLoading(false);
if (res === true) {
message.success(t('stop_model_success'));
}
}
return (
<div className="relative flex flex-col p-1 md:p-2 sm:w-1/2 lg:w-1/3">
<div className="relative flex items-center p-4 min-w-min rounded-lg justify-between bg-white border-gray-200 border hover:shadow-md dark:border-gray-600 dark:bg-black dark:hover:border-white transition-all text-black dark:text-white">
<div className="flex flex-col">
{info.healthy && (
<Tooltip title="Healthy">
<SentimentSatisfiedAltIcon className="absolute top-4 right-4 !text-3xl !text-green-600" />
</Tooltip>
)}
{!info.healthy && (
<Tooltip title="Unhealthy">
<SentimentVeryDissatisfiedIcon className="absolute top-4 right-4 !text-3xl !text-red-600" />
</Tooltip>
)}
<Tooltip title="Stop Model">
<StopCircleIcon
className="absolute right-4 bottom-4 !text-3xl !text-orange-600 cursor-pointer"
onClick={() => {
stopTheModel(info);
}}
/>
</Tooltip>
<div className="flex items-center">
{renderModelIcon(info.model_name, { width: 32, height: 32 })}
<div className="inline-block ml-2">
<h3 className="text-lg font-semibold">{info.model_name}</h3>
<h3 className="text-sm opacity-60">{info.model_type}</h3>
</div>
</div>
<div className="text-sm mt-2">
<p className="font-semibold">Host:</p>
<p className="opacity-60">{info.host}</p>
<p className="font-semibold mt-2">Manage host:</p>
<p className="opacity-60">
<span>{info.manager_host}:</span>
<span>{info.manager_port}</span>
</p>
<p className="font-semibold mt-2">Last heart beat:</p>
<p className="opacity-60">{moment(info.last_heartbeat).format('YYYY-MM-DD HH:MM:SS')}</p>
</div>
</div>
</div>
</div>
);
}
export default ModelCard;

View File

@@ -0,0 +1,100 @@
import { apiInterceptors, getSupportModels, startModel } from '@/client/api';
import { SupportModel, SupportModelParams } from '@/types/model';
import { Button, Form, Select, Tooltip, message } from 'antd';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { renderModelIcon } from '@/components/chat/header/model-selector';
import ModelParams from './model-params';
const { Option } = Select;
function ModelForm({ onCancel, onSuccess }: { onCancel: () => void; onSuccess: () => void }) {
const { t } = useTranslation();
const [models, setModels] = useState<Array<SupportModel> | null>([]);
const [selectedModel, setSelectedModel] = useState<SupportModel>();
const [params, setParams] = useState<Array<SupportModelParams> | null>(null);
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
async function getModels() {
const [, res] = await apiInterceptors(getSupportModels());
if (res && res.length) {
setModels(
res.sort((a: SupportModel, b: SupportModel) => {
if (a.enabled && !b.enabled) {
return -1;
} else if (!a.enabled && b.enabled) {
return 1;
} else {
return a.model.localeCompare(b.model);
}
}),
);
}
setModels(res);
}
useEffect(() => {
getModels();
}, []);
function handleChange(value: string, option: any) {
setSelectedModel(option.model);
setParams(option.model.params);
}
async function onFinish(values: any) {
if (!selectedModel) {
return;
}
delete values.model;
setLoading(true);
const [, , data] = await apiInterceptors(
startModel({
host: selectedModel.host,
port: selectedModel.port,
model: selectedModel.model,
worker_type: selectedModel?.worker_type,
params: values,
}),
);
setLoading(false);
if (data?.success === true) {
onSuccess && onSuccess();
return message.success(t('start_model_success'));
}
}
return (
<Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} onFinish={onFinish} form={form}>
<Form.Item label="Model" name="model" rules={[{ required: true, message: t('model_select_tips') }]}>
<Select showSearch onChange={handleChange}>
{models?.map((model) => (
<Option key={model.model} value={model.model} label={model.model} model={model} disabled={!model.enabled}>
{renderModelIcon(model.model)}
<Tooltip title={model.enabled ? model.model : t('download_model_tip')}>
<span className="ml-2">{model.model}</span>
</Tooltip>
<Tooltip title={model.enabled ? `${model.host}:${model.port}` : t('download_model_tip')}>
<p className="inline-block absolute right-4">
<span>{model.host}:</span>
<span>{model.port}</span>
</p>
</Tooltip>
</Option>
))}
</Select>
</Form.Item>
<ModelParams params={params} form={form} />
<div className="flex justify-center">
<Button type="primary" htmlType="submit" loading={loading}>
{t('submit')}
</Button>
<Button className="ml-10" onClick={onCancel}>
Cancel
</Button>
</div>
</Form>
);
}
export default ModelForm;

View File

@@ -0,0 +1,54 @@
import { SupportModelParams } from '@/types/model';
import { Checkbox, Form, FormInstance, Input, InputNumber } from 'antd';
import { useEffect } from 'react';
interface ParamValues {
[key: string]: string | number | boolean;
}
function ModelParams({ params, form }: { params: Array<SupportModelParams> | null; form: FormInstance<any> }) {
useEffect(() => {
if (params) {
const initialValues: ParamValues = {};
params.forEach((param) => {
initialValues[param.param_name] = param.default_value;
});
form.setFieldsValue(initialValues); // 设置表单字段的初始值
}
}, [params, form]);
if (!params || params?.length < 1) {
return null;
}
function renderItem(param: SupportModelParams) {
switch (param.param_type) {
case 'str':
return <Input />;
case 'int':
return <InputNumber />;
case 'bool':
return <Checkbox />;
}
}
return (
<>
{params?.map((param: SupportModelParams) => (
<Form.Item
key={param.param_name}
label={
<p className="whitespace-normal overflow-wrap-break-word">{param.description?.length > 20 ? param.param_name : param.description}</p>
}
name={param.param_name}
initialValue={param.default_value}
valuePropName={param.param_type === 'bool' ? 'checked' : 'value'}
tooltip={param.description}
rules={[{ required: param.required, message: `Please input ${param.description}` }]}
>
{renderItem(param)}
</Form.Item>
))}
</>
);
}
export default ModelParams;