From 652c73b1b359309889ae361c599f0ee867a3f487 Mon Sep 17 00:00:00 2001
From: Gavan <994259213@qq.com>
Date: Tue, 15 Jul 2025 10:16:29 +0800
Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=A8=A1=E5=9E=8B=E8=B0=83?=
=?UTF-8?q?=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/src/api/Model.ts | 32 ++
ui/src/api/types.ts | 100 +++-
ui/src/assets/fonts/iconfont.js | 2 +-
ui/src/components/form/index.tsx | 2 +-
ui/src/pages/model/components/modelCard.tsx | 10 +-
ui/src/pages/model/components/modelModal.tsx | 538 +++++++++++++------
ui/src/pages/model/constant.ts | 41 +-
ui/src/pages/model/index.tsx | 8 +-
ui/src/pages/user/dashboard/index.tsx | 32 --
9 files changed, 543 insertions(+), 222 deletions(-)
diff --git a/ui/src/api/Model.ts b/ui/src/api/Model.ts
index b6f5f6d..71a1c4b 100644
--- a/ui/src/api/Model.ts
+++ b/ui/src/api/Model.ts
@@ -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使用情况
*
diff --git a/ui/src/api/types.ts b/ui/src/api/types.ts
index 34adf65..81ae5aa 100644
--- a/ui/src/api/types.ts
+++ b/ui/src/api/types.ts
@@ -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";
diff --git a/ui/src/assets/fonts/iconfont.js b/ui/src/assets/fonts/iconfont.js
index 2b997c2..abc174c 100644
--- a/ui/src/assets/fonts/iconfont.js
+++ b/ui/src/assets/fonts/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var t,h,i,o,e,m=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}t=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?m(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),t()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(i=t,o=l.document,e=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,n())})}function n(){e||(e=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}n()}})(window);
\ No newline at end of file
+window._iconfont_svg_string_4940939='',(l=>{var a=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,e,m=function(a,c){c.parentNode.insertBefore(a,c)};if(a&&!l.__iconfont__svg__cssinject__){l.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}h=function(){var a,c=document.createElement("div");c.innerHTML=l._iconfont_svg_string_4940939,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?m(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=l.document,e=!1,z(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){e||(e=!0,i())}function z(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(z,50)}d()}})(window);
\ No newline at end of file
diff --git a/ui/src/components/form/index.tsx b/ui/src/components/form/index.tsx
index 646717b..08be17e 100644
--- a/ui/src/components/form/index.tsx
+++ b/ui/src/components/form/index.tsx
@@ -16,7 +16,7 @@ export const FormItem = ({
children,
required,
}: {
- label: string;
+ label: string | React.ReactNode;
children: React.ReactNode;
required?: boolean;
}) => {
diff --git a/ui/src/pages/model/components/modelCard.tsx b/ui/src/pages/model/components/modelCard.tsx
index 42bf7fb..e81635f 100644
--- a/ui/src/pages/model/components/modelCard.tsx
+++ b/ui/src/pages/model/components/modelCard.tsx
@@ -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 = ({
@@ -202,7 +204,7 @@ const ModelItem = ({
interface IModelCardProps {
title: string;
- modelType: 'llm' | 'coder';
+ modelType: ConstsModelType;
}
const ModelCard: React.FC = ({ title, modelType }) => {
@@ -257,7 +259,7 @@ const ModelCard: React.FC = ({ title, modelType }) => {
}}
refresh={refresh}
data={editData}
- modelType={modelType}
+ type={modelType}
/>
);
diff --git a/ui/src/pages/model/components/modelModal.tsx b/ui/src/pages/model/components/modelModal.tsx
index e6fec16..3437758 100644
--- a/ui/src/pages/model/components/modelModal.tsx
+++ b/ui/src/pages/model/components/modelModal.tsx
@@ -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 = ModelProvider;
+
const {
formState: { errors },
handleSubmit,
@@ -50,64 +64,134 @@ const ModelModal = ({
reset,
setValue,
watch,
- } = useForm>({
+ } = useForm({
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([]);
+ 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,
+ 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) => {
+ 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,
+ })
+ .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,
+ })
+ .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 (
{
- reset();
- setModelUserList([]);
- setLoading(false);
- setError('');
- onClose();
- }}
+ onCancel={handleReset}
okText='保存'
onOk={handleSubmit(onSubmit)}
okButtonProps={{
loading,
+ disabled: !success && providerBrand !== 'Other',
}}
>
@@ -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 = ({
>
模型供应商
- {modelUserList.map((it) => (
+ {Object.values(providers).map((it) => (
{
- 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: '',
+ });
+ }
}}
>
-
- {it.provider}
+
+ {it.cn || it.label || '其他'}
))}
- API 地址
+
+ API 地址{' '}
+
+ *
+
+
{
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 }}
>
-
+
API Secret
-
- {ModelProvider[providerBrand].modelDocumentUrl && (
+ {providers[providerBrand].secretRequired && (
+
+ {' '}
+ *
+
+ )}
+
+ {providers[providerBrand].modelDocumentUrl && (
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);
}}
/>
)}
/>
-
-
- 模型名称
-
-
- (
-
- {currentModelList.map((it) => (
-
- ))}
-
- )}
- />
+ {providerBrand === 'AzureOpenAI' && (
+ <>
+
+ API Version
+
+ (
+ {
+ field.onChange(e.target.value);
+ setModelUserList([]);
+ setValue('model', '');
+ setSuccess(false);
+ }}
+ />
+ )}
+ />
+ >
+ )}
+ {providerBrand === 'Other' ? (
+ <>
+
+ 模型名称{' '}
+
+ *
+
+
+ (
+
+ )}
+ />
+
+ 需要与模型供应商提供的名称完全一致,不要随便填写
+
+ >
+ ) : modelUserList.length === 0 ? (
+
+ ) : (
+ <>
+
+ 模型名称{' '}
+
+ *
+
+
+ (
+
+ {modelUserList.map((it) => (
+
+ ))}
+
+ )}
+ />
+ {providers[providerBrand].customHeader && (
+ <>
+
+ Header
+
+
+ (
+
+ )}
+ />
+ =
+ (
+
+ )}
+ />
+
+ >
+ )}
+ >
+ )}
{error && (
{
return (
-
-
+
+
);
};
diff --git a/ui/src/pages/user/dashboard/index.tsx b/ui/src/pages/user/dashboard/index.tsx
index 9185d88..6c154bd 100644
--- a/ui/src/pages/user/dashboard/index.tsx
+++ b/ui/src/pages/user/dashboard/index.tsx
@@ -17,38 +17,6 @@ const Dashboard = () => {
const [memberData, setMemberData] = useState(null);
const [timeRange, setTimeRange] = useState('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 (
From 8324ff130fa78782de2344d613dd0d5bfb38edba Mon Sep 17 00:00:00 2001
From: Gavan <994259213@qq.com>
Date: Tue, 15 Jul 2025 17:57:27 +0800
Subject: [PATCH 2/2] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E5=88=97=E8=A1=A8?=
=?UTF-8?q?=E5=B1=95=E7=A4=BA=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
ui/src/pages/completion/index.tsx | 2 +-
ui/src/pages/user/completion/index.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/ui/src/pages/completion/index.tsx b/ui/src/pages/completion/index.tsx
index 6f6e7af..2e8d16f 100644
--- a/ui/src/pages/completion/index.tsx
+++ b/ui/src/pages/completion/index.tsx
@@ -44,7 +44,7 @@ const Completion = () => {
const { data: userOptions = { users: [] } } = useRequest(() =>
getListUser({
page: 1,
- size: 10,
+ size: 9999,
})
);
diff --git a/ui/src/pages/user/completion/index.tsx b/ui/src/pages/user/completion/index.tsx
index 1f6b429..e7f19c3 100644
--- a/ui/src/pages/user/completion/index.tsx
+++ b/ui/src/pages/user/completion/index.tsx
@@ -44,7 +44,7 @@ const Completion = () => {
const { data: userOptions = { users: [] } } = useRequest(() =>
getListUser({
page: 1,
- size: 10,
+ size: 9999,
})
);