mirror of
https://github.com/chaitin/MonkeyCode.git
synced 2026-02-02 14:53:55 +08:00
@@ -15,9 +15,11 @@ import {
|
||||
DomainAllModelResp,
|
||||
DomainCheckModelReq,
|
||||
DomainCreateModelReq,
|
||||
DomainGetProviderModelListResp,
|
||||
DomainModel,
|
||||
DomainModelTokenUsageResp,
|
||||
DomainUpdateModelReq,
|
||||
GetGetProviderModelListParams,
|
||||
GetGetTokenUsageParams,
|
||||
GetMyModelListParams,
|
||||
WebResp,
|
||||
@@ -169,6 +171,36 @@ export const getMyModelList = (
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取供应商支持的模型列表
|
||||
*
|
||||
* @tags Model
|
||||
* @name GetGetProviderModelList
|
||||
* @summary 获取供应商支持的模型列表
|
||||
* @request GET:/api/v1/model/provider/supported
|
||||
* @response `200` `(WebResp & {
|
||||
data?: DomainGetProviderModelListResp,
|
||||
|
||||
})` OK
|
||||
*/
|
||||
|
||||
export const getGetProviderModelList = (
|
||||
query: GetGetProviderModelListParams,
|
||||
params: RequestParams = {},
|
||||
) =>
|
||||
request<
|
||||
WebResp & {
|
||||
data?: DomainGetProviderModelListResp;
|
||||
}
|
||||
>({
|
||||
path: `/api/v1/model/provider/supported`,
|
||||
method: "GET",
|
||||
query: query,
|
||||
type: ContentType.Json,
|
||||
format: "json",
|
||||
...params,
|
||||
});
|
||||
|
||||
/**
|
||||
* @description 获取模型token使用情况
|
||||
*
|
||||
|
||||
@@ -35,6 +35,19 @@ export enum ConstsModelStatus {
|
||||
ModelStatusInactive = "inactive",
|
||||
}
|
||||
|
||||
export enum ConstsModelProvider {
|
||||
ModelProviderSiliconFlow = "SiliconFlow",
|
||||
ModelProviderOpenAI = "OpenAI",
|
||||
ModelProviderOllama = "Ollama",
|
||||
ModelProviderDeepSeek = "DeepSeek",
|
||||
ModelProviderMoonshot = "Moonshot",
|
||||
ModelProviderAzureOpenAI = "AzureOpenAI",
|
||||
ModelProviderBaiZhiCloud = "BaiZhiCloud",
|
||||
ModelProviderHunyuan = "Hunyuan",
|
||||
ModelProviderBaiLian = "BaiLian",
|
||||
ModelProviderVolcengine = "Volcengine",
|
||||
}
|
||||
|
||||
export enum ConstsChatRole {
|
||||
ChatRoleUser = "user",
|
||||
ChatRoleAssistant = "assistant",
|
||||
@@ -133,12 +146,15 @@ export interface DomainChatRecord {
|
||||
export interface DomainCheckModelReq {
|
||||
/** 接口地址 */
|
||||
api_base: string;
|
||||
api_header?: string;
|
||||
/** 接口密钥 */
|
||||
api_key: string;
|
||||
api_version?: string;
|
||||
/** 模型名称 */
|
||||
model_name: string;
|
||||
/** 提供商 */
|
||||
provider: string;
|
||||
provider: ConstsModelProvider;
|
||||
type: "llm" | "coder" | "embedding" | "rerank";
|
||||
}
|
||||
|
||||
export interface DomainCompletionInfo {
|
||||
@@ -174,15 +190,29 @@ export interface DomainCreateAdminReq {
|
||||
|
||||
export interface DomainCreateModelReq {
|
||||
/** 接口地址 如:https://api.qwen.com */
|
||||
api_base?: string;
|
||||
api_base: string;
|
||||
api_header?: string;
|
||||
/** 接口密钥 如:sk-xxxx */
|
||||
api_key?: string;
|
||||
api_version?: string;
|
||||
/** 模型名称 如: deepseek-v3 */
|
||||
model_name?: string;
|
||||
model_name: string;
|
||||
/** 模型类型 llm:对话模型 coder:代码模型 */
|
||||
model_type?: ConstsModelType;
|
||||
/** 提供商 */
|
||||
provider?: string;
|
||||
provider:
|
||||
| "SiliconFlow"
|
||||
| "OpenAI"
|
||||
| "Ollama"
|
||||
| "DeepSeek"
|
||||
| "Moonshot"
|
||||
| "AzureOpenAI"
|
||||
| "BaiZhiCloud"
|
||||
| "Hunyuan"
|
||||
| "BaiLian"
|
||||
| "Volcengine";
|
||||
/** 模型显示名称 */
|
||||
show_name?: string;
|
||||
}
|
||||
|
||||
export interface DomainCustomOAuth {
|
||||
@@ -253,6 +283,10 @@ export interface DomainDingtalkOAuthReq {
|
||||
enable?: boolean;
|
||||
}
|
||||
|
||||
export interface DomainGetProviderModelListResp {
|
||||
models?: DomainProviderModelListItem[];
|
||||
}
|
||||
|
||||
export interface DomainIPInfo {
|
||||
/** ASN */
|
||||
asn?: string;
|
||||
@@ -332,8 +366,12 @@ export interface DomainLoginResp {
|
||||
export interface DomainModel {
|
||||
/** 接口地址 如:https://api.qwen.com */
|
||||
api_base?: string;
|
||||
/** 接口头 如:Authorization: Bearer sk-xxxx */
|
||||
api_header?: string;
|
||||
/** 接口密钥 如:sk-xxxx */
|
||||
api_key?: string;
|
||||
/** 接口版本 如:2023-05-15 */
|
||||
api_version?: string;
|
||||
/** 创建时间 */
|
||||
created_at?: number;
|
||||
/** 模型ID */
|
||||
@@ -349,7 +387,9 @@ export interface DomainModel {
|
||||
/** 输出token数 */
|
||||
output?: number;
|
||||
/** 提供商 */
|
||||
provider?: string;
|
||||
provider?: ConstsModelProvider;
|
||||
/** 模型显示名称 */
|
||||
show_name?: string;
|
||||
/** 状态 active:启用 inactive:禁用 */
|
||||
status?: ConstsModelStatus;
|
||||
/** 更新时间 */
|
||||
@@ -362,7 +402,17 @@ export interface DomainModelBasic {
|
||||
/** 模型名称 */
|
||||
name?: string;
|
||||
/** 提供商 */
|
||||
provider?: string;
|
||||
provider:
|
||||
| "SiliconFlow"
|
||||
| "OpenAI"
|
||||
| "Ollama"
|
||||
| "DeepSeek"
|
||||
| "Moonshot"
|
||||
| "AzureOpenAI"
|
||||
| "BaiZhiCloud"
|
||||
| "Hunyuan"
|
||||
| "BaiLian"
|
||||
| "Volcengine";
|
||||
}
|
||||
|
||||
export interface DomainModelData {
|
||||
@@ -411,6 +461,10 @@ export interface DomainProviderModel {
|
||||
provider?: string;
|
||||
}
|
||||
|
||||
export interface DomainProviderModelListItem {
|
||||
model?: string;
|
||||
}
|
||||
|
||||
export interface DomainRegisterReq {
|
||||
/** 邀请码 */
|
||||
code: string;
|
||||
@@ -506,14 +560,28 @@ export interface DomainTimeStat {
|
||||
export interface DomainUpdateModelReq {
|
||||
/** 接口地址 如:https://api.qwen.com */
|
||||
api_base?: string;
|
||||
api_header?: string;
|
||||
/** 接口密钥 如:sk-xxxx */
|
||||
api_key?: string;
|
||||
api_version?: string;
|
||||
/** 模型ID */
|
||||
id?: string;
|
||||
/** 模型名称 */
|
||||
model_name?: string;
|
||||
/** 提供商 */
|
||||
provider?: string;
|
||||
provider:
|
||||
| "SiliconFlow"
|
||||
| "OpenAI"
|
||||
| "Ollama"
|
||||
| "DeepSeek"
|
||||
| "Moonshot"
|
||||
| "AzureOpenAI"
|
||||
| "BaiZhiCloud"
|
||||
| "Hunyuan"
|
||||
| "BaiLian"
|
||||
| "Volcengine";
|
||||
/** 模型显示名称 */
|
||||
show_name?: string;
|
||||
/** 状态 active:启用 inactive:禁用 */
|
||||
status?: ConstsModelStatus;
|
||||
}
|
||||
@@ -805,6 +873,24 @@ export interface GetMyModelListParams {
|
||||
model_type?: "llm" | "coder" | "embedding" | "audio" | "reranker";
|
||||
}
|
||||
|
||||
export interface GetGetProviderModelListParams {
|
||||
api_header?: string;
|
||||
api_key?: string;
|
||||
base_url: string;
|
||||
provider:
|
||||
| "SiliconFlow"
|
||||
| "OpenAI"
|
||||
| "Ollama"
|
||||
| "DeepSeek"
|
||||
| "Moonshot"
|
||||
| "AzureOpenAI"
|
||||
| "BaiZhiCloud"
|
||||
| "Hunyuan"
|
||||
| "BaiLian"
|
||||
| "Volcengine";
|
||||
type: "llm" | "coder" | "embedding" | "audio" | "reranker";
|
||||
}
|
||||
|
||||
export interface GetGetTokenUsageParams {
|
||||
/** 模型类型 llm:对话模型 coder:代码模型 */
|
||||
model_type: "llm" | "coder" | "embedding" | "audio" | "reranker";
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -16,7 +16,7 @@ export const FormItem = ({
|
||||
children,
|
||||
required,
|
||||
}: {
|
||||
label: string;
|
||||
label: string | React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
required?: boolean;
|
||||
}) => {
|
||||
|
||||
@@ -44,7 +44,7 @@ const Completion = () => {
|
||||
const { data: userOptions = { users: [] } } = useRequest(() =>
|
||||
getListUser({
|
||||
page: 1,
|
||||
size: 10,
|
||||
size: 9999,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
||||
import Card from '@/components/card';
|
||||
import { useRequest } from 'ahooks';
|
||||
import { getMyModelList, putUpdateModel } from '@/api/Model';
|
||||
import { DomainModel, ConstsModelStatus } from '@/api/types';
|
||||
import { DomainModel, ConstsModelStatus, ConstsModelType } from '@/api/types';
|
||||
import { Stack, Box, Button, Grid2 as Grid, ButtonBase } from '@mui/material';
|
||||
import StyledLabel from '@/components/label';
|
||||
import { Icon, Modal, message } from '@c-x/ui';
|
||||
@@ -40,6 +40,7 @@ const ModelItem = ({
|
||||
putUpdateModel({
|
||||
id: data.id,
|
||||
status: ConstsModelStatus.ModelStatusInactive,
|
||||
provider: data.provider!,
|
||||
}).then(() => {
|
||||
message.success('停用成功');
|
||||
refresh();
|
||||
@@ -64,6 +65,7 @@ const ModelItem = ({
|
||||
putUpdateModel({
|
||||
id: data.id,
|
||||
status: ConstsModelStatus.ModelStatusActive,
|
||||
provider: data.provider!,
|
||||
}).then(() => {
|
||||
message.success('激活成功');
|
||||
refresh();
|
||||
@@ -110,7 +112,7 @@ const ModelItem = ({
|
||||
<Stack direction='row' alignItems='center' gap={1}>
|
||||
<Icon
|
||||
type={
|
||||
ModelProvider[data.provider as keyof typeof ModelProvider].icon
|
||||
ModelProvider[data.provider as keyof typeof ModelProvider]?.icon
|
||||
}
|
||||
sx={{ fontSize: 24 }}
|
||||
/>
|
||||
@@ -202,7 +204,7 @@ const ModelItem = ({
|
||||
|
||||
interface IModelCardProps {
|
||||
title: string;
|
||||
modelType: 'llm' | 'coder';
|
||||
modelType: ConstsModelType;
|
||||
}
|
||||
|
||||
const ModelCard: React.FC<IModelCardProps> = ({ title, modelType }) => {
|
||||
@@ -257,7 +259,7 @@ const ModelCard: React.FC<IModelCardProps> = ({ title, modelType }) => {
|
||||
}}
|
||||
refresh={refresh}
|
||||
data={editData}
|
||||
modelType={modelType}
|
||||
type={modelType}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
|
||||
@@ -1,48 +1,62 @@
|
||||
import {
|
||||
postCreateModel,
|
||||
putUpdateModel,
|
||||
getGetProviderModelList,
|
||||
postCheckModel,
|
||||
getListModel,
|
||||
} from '@/api/Model';
|
||||
putUpdateModel,
|
||||
} from '@/api';
|
||||
import { ConstsModelType, DomainModel } from '@/api/types';
|
||||
import Card from '@/components/card';
|
||||
import { ModelProvider } from '../constant';
|
||||
import { Icon, message, Modal } from '@c-x/ui';
|
||||
import { StyledFormLabel } from '@/components/form';
|
||||
import { ModelProvider } from '@/pages/model/constant';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
MenuItem,
|
||||
Stack,
|
||||
TextField,
|
||||
useTheme,
|
||||
alpha,
|
||||
alpha as addOpacityToColor,
|
||||
} from '@mui/material';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Icon, message, Modal } from '@c-x/ui';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import {
|
||||
ConstsModelType,
|
||||
DomainUpdateModelReq,
|
||||
DomainCreateModelReq,
|
||||
DomainProviderModel,
|
||||
} from '@/api/types';
|
||||
|
||||
type ModelItem = any;
|
||||
|
||||
interface AddModelProps {
|
||||
open: boolean;
|
||||
data?: ModelItem;
|
||||
data: DomainModel | null;
|
||||
type: ConstsModelType;
|
||||
onClose: () => void;
|
||||
refresh: () => void;
|
||||
modelType: 'llm' | 'coder';
|
||||
}
|
||||
|
||||
const ModelModal = ({
|
||||
interface AddModelForm {
|
||||
provider: keyof typeof ModelProvider;
|
||||
model: string;
|
||||
base_url: string;
|
||||
api_version: string;
|
||||
api_key: string;
|
||||
api_header_key: string;
|
||||
api_header_value: string;
|
||||
type: ConstsModelType;
|
||||
}
|
||||
|
||||
const titleMap = {
|
||||
[ConstsModelType.ModelTypeLLM]: '对话模型',
|
||||
[ConstsModelType.ModelTypeCoder]: '代码补全模型',
|
||||
[ConstsModelType.ModelTypeEmbedding]: '向量模型',
|
||||
[ConstsModelType.ModelTypeAudio]: '音频模型',
|
||||
[ConstsModelType.ModelTypeReranker]: '重排序模型',
|
||||
};
|
||||
|
||||
const ModelAdd = ({
|
||||
open,
|
||||
onClose,
|
||||
refresh,
|
||||
data,
|
||||
modelType,
|
||||
type = ConstsModelType.ModelTypeLLM,
|
||||
}: AddModelProps) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const providers: Record<string, any> = ModelProvider;
|
||||
|
||||
const {
|
||||
formState: { errors },
|
||||
handleSubmit,
|
||||
@@ -50,64 +64,134 @@ const ModelModal = ({
|
||||
reset,
|
||||
setValue,
|
||||
watch,
|
||||
} = useForm<Required<DomainCreateModelReq>>({
|
||||
} = useForm<AddModelForm>({
|
||||
defaultValues: {
|
||||
provider: data?.provider || 'DeepSeek',
|
||||
api_base: data?.api_base || ModelProvider.DeepSeek.defaultBaseUrl,
|
||||
model_name: data?.model_name || '',
|
||||
api_key: data?.api_key || '',
|
||||
type,
|
||||
provider: 'BaiZhiCloud',
|
||||
base_url: providers['BaiZhiCloud'].defaultBaseUrl,
|
||||
model: '',
|
||||
api_version: '',
|
||||
api_key: '',
|
||||
api_header_key: '',
|
||||
api_header_value: '',
|
||||
},
|
||||
});
|
||||
|
||||
const providerBrand = watch('provider') as keyof typeof ModelProvider;
|
||||
const providerBrand = watch('provider');
|
||||
|
||||
const [modelUserList, setModelUserList] = useState<DomainProviderModel[]>([]);
|
||||
const [modelUserList, setModelUserList] = useState<{ model: string }[]>([]);
|
||||
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [modelLoading, setModelLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
const onCreateModel = (value: DomainCreateModelReq) => {
|
||||
return postCreateModel({
|
||||
...value,
|
||||
model_type: modelType as ConstsModelType,
|
||||
}).then(() => {
|
||||
message.success('添加成功');
|
||||
reset();
|
||||
onClose();
|
||||
refresh();
|
||||
const handleReset = () => {
|
||||
onClose();
|
||||
reset({
|
||||
type,
|
||||
provider: 'BaiZhiCloud',
|
||||
model: '',
|
||||
base_url: '',
|
||||
api_key: '',
|
||||
api_version: '',
|
||||
api_header_key: '',
|
||||
api_header_value: '',
|
||||
});
|
||||
setModelUserList([]);
|
||||
setSuccess(false);
|
||||
setLoading(false);
|
||||
setModelLoading(false);
|
||||
setError('');
|
||||
refresh();
|
||||
};
|
||||
|
||||
const onUpdateModel = (value: DomainUpdateModelReq) => {
|
||||
return putUpdateModel({
|
||||
...value,
|
||||
}).then(() => {
|
||||
message.success('修改成功');
|
||||
reset();
|
||||
onClose();
|
||||
refresh();
|
||||
});
|
||||
const getModel = (value: AddModelForm) => {
|
||||
let header = '';
|
||||
if (value.api_header_key && value.api_header_value) {
|
||||
header = value.api_header_key + '=' + value.api_header_value;
|
||||
}
|
||||
setModelLoading(true);
|
||||
getGetProviderModelList({
|
||||
type,
|
||||
api_key: value.api_key,
|
||||
base_url: value.base_url,
|
||||
provider: value.provider as Exclude<typeof value.provider, 'Other'>,
|
||||
api_header: header,
|
||||
})
|
||||
.then((res) => {
|
||||
setModelUserList(
|
||||
(res.models || [])
|
||||
.filter((item): item is { model: string } => !!item.model)
|
||||
.sort((a, b) => a.model!.localeCompare(b.model!))
|
||||
);
|
||||
if (
|
||||
data &&
|
||||
(res.models || []).find((it) => it.model === data.model_name)
|
||||
) {
|
||||
setValue('model', data.model_name!);
|
||||
} else {
|
||||
setValue('model', res.models?.[0]?.model || '');
|
||||
}
|
||||
setSuccess(true);
|
||||
})
|
||||
.finally(() => {
|
||||
setModelLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
const onSubmit = (value: Required<DomainCreateModelReq>) => {
|
||||
const onSubmit = (value: AddModelForm) => {
|
||||
let header = '';
|
||||
if (value.api_header_key && value.api_header_value) {
|
||||
header = value.api_header_key + '=' + value.api_header_value;
|
||||
}
|
||||
setError('');
|
||||
setLoading(true);
|
||||
postCheckModel({
|
||||
...value,
|
||||
// @ts-ignore
|
||||
type,
|
||||
api_key: value.api_key,
|
||||
api_base: value.base_url,
|
||||
api_version: value.api_version,
|
||||
// @ts-ignore
|
||||
provider: value.provider,
|
||||
model_name: value.model,
|
||||
api_header: header,
|
||||
})
|
||||
.then((res) => {
|
||||
if (data) {
|
||||
onUpdateModel({
|
||||
...value,
|
||||
putUpdateModel({
|
||||
api_key: value.api_key,
|
||||
api_base: value.base_url,
|
||||
model_name: value.model,
|
||||
api_header: header,
|
||||
api_version: value.api_version,
|
||||
id: data.id,
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
provider: value.provider as Exclude<typeof value.provider, 'Other'>,
|
||||
})
|
||||
.then(() => {
|
||||
message.success('修改成功');
|
||||
handleReset();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
} else {
|
||||
onCreateModel(value).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
postCreateModel({
|
||||
model_type: type,
|
||||
api_key: value.api_key,
|
||||
api_base: value.base_url,
|
||||
model_name: value.model,
|
||||
api_header: header,
|
||||
provider: value.provider as Exclude<typeof value.provider, 'Other'>,
|
||||
})
|
||||
.then(() => {
|
||||
message.success('添加成功');
|
||||
handleReset();
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
@@ -115,72 +199,64 @@ const ModelModal = ({
|
||||
});
|
||||
};
|
||||
|
||||
const resetCurData = (value: DomainModel) => {
|
||||
// @ts-ignore
|
||||
if (value.provider && value.provider !== 'Other') {
|
||||
getModel({
|
||||
api_key: value.api_key || '',
|
||||
base_url: value.api_base || '',
|
||||
model: value.model_name || '',
|
||||
provider: value.provider,
|
||||
api_version: value.api_version || '',
|
||||
api_header_key: value.api_header?.split('=')[0] || '',
|
||||
api_header_value: value.api_header?.split('=')[1] || '',
|
||||
type,
|
||||
});
|
||||
}
|
||||
reset({
|
||||
type,
|
||||
provider: value.provider || 'Other',
|
||||
model: value.model_name || '',
|
||||
base_url: value.api_base || '',
|
||||
api_key: value.api_key || '',
|
||||
api_version: value.api_version || '',
|
||||
api_header_key: value.api_header?.split('=')[0] || '',
|
||||
api_header_value: value.api_header?.split('=')[1] || '',
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
if (data) {
|
||||
reset(
|
||||
{
|
||||
provider: data.provider || 'Other',
|
||||
model_name: data.model_name || '',
|
||||
api_base: data.api_base || '',
|
||||
api_key: data.api_key || '',
|
||||
},
|
||||
{
|
||||
keepDefaultValues: true,
|
||||
}
|
||||
);
|
||||
console.log(data);
|
||||
resetCurData(data);
|
||||
} else {
|
||||
reset();
|
||||
reset({
|
||||
type,
|
||||
provider: 'BaiZhiCloud',
|
||||
model: '',
|
||||
base_url: providers['BaiZhiCloud'].defaultBaseUrl,
|
||||
api_key: '',
|
||||
api_version: '',
|
||||
api_header_key: '',
|
||||
api_header_value: '',
|
||||
});
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data, open]);
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
getListModel().then((res) => {
|
||||
setModelUserList(res.providers || []);
|
||||
if (!data) {
|
||||
setValue('provider', res.providers?.[0].provider || 'DeepSeek');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
const currentModelList = useMemo(() => {
|
||||
return (
|
||||
modelUserList.find((it) => it.provider === providerBrand)?.models || []
|
||||
);
|
||||
}, [modelUserList, providerBrand]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currentModelList.length > 0) {
|
||||
if (data) {
|
||||
setValue('api_base', data.api_base || '');
|
||||
setValue('model_name', data.model_name || '');
|
||||
} else {
|
||||
setValue('api_base', currentModelList[0].api_base || '');
|
||||
setValue('model_name', currentModelList[0].name || '');
|
||||
}
|
||||
}
|
||||
}, [currentModelList, data]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={data ? '修改第三方模型' : '添加第三方模型'}
|
||||
title={data ? `修改${titleMap[type]}` : `添加${titleMap[type]}`}
|
||||
open={open}
|
||||
width={800}
|
||||
onCancel={() => {
|
||||
reset();
|
||||
setModelUserList([]);
|
||||
setLoading(false);
|
||||
setError('');
|
||||
onClose();
|
||||
}}
|
||||
onCancel={handleReset}
|
||||
okText='保存'
|
||||
onOk={handleSubmit(onSubmit)}
|
||||
okButtonProps={{
|
||||
loading,
|
||||
disabled: !success && providerBrand !== 'Other',
|
||||
}}
|
||||
>
|
||||
<Stack direction={'row'} alignItems={'stretch'} gap={3}>
|
||||
@@ -189,7 +265,7 @@ const ModelModal = ({
|
||||
sx={{
|
||||
width: 200,
|
||||
flexShrink: 0,
|
||||
bgcolor: 'rgb(248, 249, 250)',
|
||||
bgcolor: 'background.paper2',
|
||||
borderRadius: '10px',
|
||||
p: 1,
|
||||
}}
|
||||
@@ -199,12 +275,12 @@ const ModelModal = ({
|
||||
>
|
||||
模型供应商
|
||||
</Box>
|
||||
{modelUserList.map((it) => (
|
||||
{Object.values(providers).map((it) => (
|
||||
<Stack
|
||||
direction={'row'}
|
||||
alignItems={'center'}
|
||||
gap={1.5}
|
||||
key={it.provider}
|
||||
key={it.label}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
fontSize: 14,
|
||||
@@ -213,8 +289,8 @@ const ModelModal = ({
|
||||
borderRadius: '10px',
|
||||
fontWeight: 'bold',
|
||||
fontFamily: 'Gbold',
|
||||
...(providerBrand === it.provider && {
|
||||
bgcolor: alpha(theme.palette.primary.main, 0.1),
|
||||
...(providerBrand === it.label && {
|
||||
bgcolor: addOpacityToColor(theme.palette.primary.main, 0.1),
|
||||
color: 'primary.main',
|
||||
}),
|
||||
'&:hover': {
|
||||
@@ -222,36 +298,41 @@ const ModelModal = ({
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
if (data) return;
|
||||
setError('');
|
||||
reset(
|
||||
{
|
||||
provider: it.provider as keyof typeof ModelProvider,
|
||||
api_base: '',
|
||||
model_name: '',
|
||||
if (data && data.provider === it.label) {
|
||||
resetCurData(data);
|
||||
} else {
|
||||
setModelUserList([]);
|
||||
setError('');
|
||||
setModelLoading(false);
|
||||
setSuccess(false);
|
||||
reset({
|
||||
provider: it.label as keyof typeof ModelProvider,
|
||||
base_url:
|
||||
it.label === 'AzureOpenAI' ? '' : it.defaultBaseUrl,
|
||||
model: '',
|
||||
api_version: '',
|
||||
api_key: '',
|
||||
},
|
||||
{
|
||||
keepDefaultValues: true,
|
||||
}
|
||||
);
|
||||
api_header_key: '',
|
||||
api_header_value: '',
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
type={
|
||||
ModelProvider[it.provider as keyof typeof ModelProvider].icon
|
||||
}
|
||||
sx={{ fontSize: 18 }}
|
||||
/>
|
||||
{it.provider}
|
||||
<Icon type={it.icon} sx={{ fontSize: 18 }} />
|
||||
{it.cn || it.label || '其他'}
|
||||
</Stack>
|
||||
))}
|
||||
</Stack>
|
||||
<Box sx={{ flex: 1 }}>
|
||||
<StyledFormLabel required>API 地址</StyledFormLabel>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px' }}>
|
||||
API 地址{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_base'
|
||||
name='base_url'
|
||||
rules={{
|
||||
required: {
|
||||
value: true,
|
||||
@@ -262,15 +343,16 @@ const ModelModal = ({
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
disabled={!ModelProvider[providerBrand].urlWrite}
|
||||
disabled={!providers[providerBrand].urlWrite}
|
||||
size='small'
|
||||
placeholder={ModelProvider[providerBrand].defaultBaseUrl}
|
||||
error={!!errors.api_base}
|
||||
helperText={errors.api_base?.message}
|
||||
placeholder={providers[providerBrand].defaultBaseUrl}
|
||||
error={!!errors.base_url}
|
||||
helperText={errors.base_url?.message}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
setModelUserList([]);
|
||||
setValue('model_name', '');
|
||||
setValue('model', '');
|
||||
setSuccess(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -281,12 +363,16 @@ const ModelModal = ({
|
||||
justifyContent={'space-between'}
|
||||
sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}
|
||||
>
|
||||
<StyledFormLabel
|
||||
required={ModelProvider[providerBrand].secretRequired}
|
||||
>
|
||||
<Box>
|
||||
API Secret
|
||||
</StyledFormLabel>
|
||||
{ModelProvider[providerBrand].modelDocumentUrl && (
|
||||
{providers[providerBrand].secretRequired && (
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
{' '}
|
||||
*
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
{providers[providerBrand].modelDocumentUrl && (
|
||||
<Box
|
||||
component={'span'}
|
||||
sx={{
|
||||
@@ -297,7 +383,7 @@ const ModelModal = ({
|
||||
}}
|
||||
onClick={() =>
|
||||
window.open(
|
||||
ModelProvider[providerBrand].modelDocumentUrl,
|
||||
providers[providerBrand].modelDocumentUrl,
|
||||
'_blank'
|
||||
)
|
||||
}
|
||||
@@ -311,7 +397,7 @@ const ModelModal = ({
|
||||
name='api_key'
|
||||
rules={{
|
||||
required: {
|
||||
value: ModelProvider[providerBrand].secretRequired,
|
||||
value: providers[providerBrand].secretRequired,
|
||||
message: 'API Secret 不能为空',
|
||||
},
|
||||
}}
|
||||
@@ -325,36 +411,148 @@ const ModelModal = ({
|
||||
helperText={errors.api_key?.message}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
setModelUserList([]);
|
||||
setValue('model', '');
|
||||
setSuccess(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Box sx={{ mt: 2 }}>
|
||||
<StyledFormLabel required>模型名称</StyledFormLabel>
|
||||
</Box>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name='model_name'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
select
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model_name}
|
||||
helperText={errors.model_name?.message}
|
||||
>
|
||||
{currentModelList.map((it) => (
|
||||
<MenuItem key={it.name} value={it.name}>
|
||||
{it.name}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
{providerBrand === 'AzureOpenAI' && (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
API Version
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_version'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='2024-10-21'
|
||||
error={!!errors.api_version}
|
||||
helperText={errors.api_version?.message}
|
||||
onChange={(e) => {
|
||||
field.onChange(e.target.value);
|
||||
setModelUserList([]);
|
||||
setValue('model', '');
|
||||
setSuccess(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{providerBrand === 'Other' ? (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
模型名称{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='model'
|
||||
rules={{
|
||||
required: '模型名称不能为空',
|
||||
}}
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model}
|
||||
helperText={errors.model?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ fontSize: 12, color: 'error.main', mt: 1 }}>
|
||||
需要与模型供应商提供的名称完全一致,不要随便填写
|
||||
</Box>
|
||||
</>
|
||||
) : modelUserList.length === 0 ? (
|
||||
<Button
|
||||
fullWidth
|
||||
variant='outlined'
|
||||
loading={modelLoading}
|
||||
sx={{ mt: 4 }}
|
||||
onClick={handleSubmit(getModel)}
|
||||
>
|
||||
获取模型列表
|
||||
</Button>
|
||||
) : (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
模型名称{' '}
|
||||
<Box component={'span'} sx={{ color: 'red' }}>
|
||||
*
|
||||
</Box>
|
||||
</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='model'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
select
|
||||
size='small'
|
||||
placeholder=''
|
||||
error={!!errors.model}
|
||||
helperText={errors.model?.message}
|
||||
>
|
||||
{modelUserList.map((it) => (
|
||||
<MenuItem key={it.model} value={it.model}>
|
||||
{it.model}
|
||||
</MenuItem>
|
||||
))}
|
||||
</TextField>
|
||||
)}
|
||||
/>
|
||||
{providers[providerBrand].customHeader && (
|
||||
<>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '32px', mt: 2 }}>
|
||||
Header
|
||||
</Box>
|
||||
<Stack direction={'row'} gap={1}>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_header_key'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='key'
|
||||
error={!!errors.api_header_key}
|
||||
helperText={errors.api_header_key?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Box sx={{ fontSize: 14, lineHeight: '36px' }}>=</Box>
|
||||
<Controller
|
||||
control={control}
|
||||
name='api_header_value'
|
||||
render={({ field }) => (
|
||||
<TextField
|
||||
{...field}
|
||||
fullWidth
|
||||
size='small'
|
||||
placeholder='value'
|
||||
error={!!errors.api_header_value}
|
||||
helperText={errors.api_header_value?.message}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Stack>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{error && (
|
||||
<Card
|
||||
sx={{
|
||||
@@ -376,4 +574,4 @@ const ModelModal = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default ModelModal;
|
||||
export default ModelAdd;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
export const ModelProvider = {
|
||||
百智云: {
|
||||
label: '百智云',
|
||||
cn: '',
|
||||
BaiZhiCloud: {
|
||||
label: 'BaiZhiCloud',
|
||||
cn: '百智云',
|
||||
icon: 'icon-baizhiyunlogo',
|
||||
urlWrite: false,
|
||||
secretRequired: true,
|
||||
customHeader: false,
|
||||
modelDocumentUrl: 'https://platform.deepseek.com/api_keys',
|
||||
defaultBaseUrl: 'https://api.deepseek.com/v1',
|
||||
modelDocumentUrl: 'https://model-square.app.baizhi.cloud/token',
|
||||
defaultBaseUrl: 'https://model-square.app.baizhi.cloud/v1',
|
||||
},
|
||||
DeepSeek: {
|
||||
label: 'DeepSeek',
|
||||
@@ -19,6 +19,37 @@ export const ModelProvider = {
|
||||
modelDocumentUrl: 'https://platform.deepseek.com/api_keys',
|
||||
defaultBaseUrl: 'https://api.deepseek.com/v1',
|
||||
},
|
||||
Hunyuan: {
|
||||
label: 'Hunyuan',
|
||||
cn: '腾讯混元',
|
||||
icon: 'icon-tengxunhunyuan',
|
||||
urlWrite: false,
|
||||
secretRequired: true,
|
||||
customHeader: false,
|
||||
modelDocumentUrl: 'https://console.cloud.tencent.com/hunyuan/start',
|
||||
defaultBaseUrl: 'https://api.hunyuan.cloud.tencent.com/v1',
|
||||
},
|
||||
BaiLian: {
|
||||
label: 'BaiLian',
|
||||
cn: '阿里云百炼',
|
||||
icon: 'icon-aliyunbailian',
|
||||
urlWrite: false,
|
||||
secretRequired: true,
|
||||
customHeader: false,
|
||||
modelDocumentUrl: 'https://bailian.console.aliyun.com/?tab=model#/api-key',
|
||||
defaultBaseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||||
},
|
||||
Volcengine: {
|
||||
label: 'Volcengine',
|
||||
cn: '火山引擎',
|
||||
icon: 'icon-huoshanyinqing',
|
||||
urlWrite: false,
|
||||
secretRequired: true,
|
||||
customHeader: false,
|
||||
modelDocumentUrl:
|
||||
'https://console.volcengine.com/ark/region:ark+cn-beijing/apiKey',
|
||||
defaultBaseUrl: 'https://ark.cn-beijing.volces.com/api/v3',
|
||||
},
|
||||
OpenAI: {
|
||||
label: 'OpenAI',
|
||||
cn: '',
|
||||
|
||||
@@ -2,13 +2,17 @@ import React from 'react';
|
||||
import TokenUsage from './components/tokenUsage';
|
||||
import ModelCard from './components/modelCard';
|
||||
import { Stack } from '@mui/material';
|
||||
import { ConstsModelType } from '@/api/types';
|
||||
|
||||
const Model = () => {
|
||||
return (
|
||||
<Stack gap={2}>
|
||||
<TokenUsage />
|
||||
<ModelCard title='对话模型' modelType='llm' />
|
||||
<ModelCard title='代码补全模型' modelType='coder' />
|
||||
<ModelCard title='对话模型' modelType={ConstsModelType.ModelTypeLLM} />
|
||||
<ModelCard
|
||||
title='代码补全模型'
|
||||
modelType={ConstsModelType.ModelTypeCoder}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -44,7 +44,7 @@ const Completion = () => {
|
||||
const { data: userOptions = { users: [] } } = useRequest(() =>
|
||||
getListUser({
|
||||
page: 1,
|
||||
size: 10,
|
||||
size: 9999,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
@@ -17,38 +17,6 @@ const Dashboard = () => {
|
||||
const [memberData, setMemberData] = useState<DomainUser | null>(null);
|
||||
const [timeRange, setTimeRange] = useState<TimeRange>('24h');
|
||||
|
||||
const { data: userData, refresh } = useRequest(
|
||||
() =>
|
||||
getListUser({
|
||||
page: 1,
|
||||
size: 99999,
|
||||
}),
|
||||
{
|
||||
manual: true,
|
||||
onSuccess: (res) => {
|
||||
if (id) {
|
||||
setMemberData(res.users?.find((item) => item.id === id) || null);
|
||||
} else {
|
||||
setMemberData(res.users?.[0] || null);
|
||||
navigate(`/dashboard/member/${res.users?.[0]?.id}`);
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
const userList = useMemo(() => {
|
||||
return userData?.users || [];
|
||||
}, [userData]);
|
||||
useEffect(() => {
|
||||
if (tabValue === 'member') {
|
||||
refresh();
|
||||
}
|
||||
}, [tabValue]);
|
||||
|
||||
const onMemberChange = (data: DomainUser) => {
|
||||
setMemberData(data);
|
||||
navigate(`/dashboard/member/${data.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<Stack gap={2} sx={{ height: '100%' }}>
|
||||
<Stack direction='row' justifyContent='space-between' alignItems='center'>
|
||||
|
||||
Reference in New Issue
Block a user