chore: provider form test

This commit is contained in:
jialin
2026-02-03 18:21:08 +08:00
parent d86d061f97
commit e188d788b9
27 changed files with 448 additions and 317 deletions
+3
View File
@@ -27,6 +27,7 @@ const SealSelect: React.FC<
loading,
footer,
alwaysFocus = false,
styles,
...rest
} = props;
const intl = useIntl();
@@ -109,6 +110,8 @@ const SealSelect: React.FC<
>
<BaseSelect
{...rest}
styles={styles}
placeholder={placeholder}
footer={footer}
ref={inputRef}
options={children ? null : _options}
+2 -1
View File
@@ -4,7 +4,8 @@ export enum PageAction {
CREATE = 'create',
UPDATE = 'update',
VIEW = 'view',
EDIT = 'edit'
EDIT = 'edit',
COPY = 'copy'
}
export const StatusColorMap: Record<
+1 -1
View File
@@ -6,7 +6,7 @@ export interface DropDownItem {
iconfont?: boolean;
}
export type PageActionType = 'create' | 'update' | 'view' | 'edit';
export type PageActionType = 'create' | 'update' | 'view' | 'edit' | 'copy';
export type StatusType =
| 'error'
+2 -1
View File
@@ -11,5 +11,6 @@ export default {
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
'providers.form.model.test': 'Test',
'providers.form.model.test.tips': 'Test Connection'
};
+2 -1
View File
@@ -11,5 +11,6 @@ export default {
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
'providers.form.model.test': 'Test',
'providers.form.model.test.tips': 'Test Connection'
};
+2 -1
View File
@@ -11,5 +11,6 @@ export default {
'providers.form.proxy.url': 'Proxy URL',
'providers.form.proxy.timeout': 'Proxy Timeout (seconds)',
'providers.form.customConfig': 'Custom Configuration',
'providers.form.model.test': 'Test Connection'
'providers.form.model.test': 'Test',
'providers.form.model.test.tips': 'Test Connection'
};
+3 -2
View File
@@ -8,8 +8,9 @@ export default {
'providers.form.tokens.add': '添加 API 令牌',
'providers.form.models.add': '添加模型',
'providers.form.proxy.enable': '启用代理',
'providers.form.proxy.url': '代理 URL',
'providers.form.proxy.url': '代理地址',
'providers.form.proxy.timeout': '代理超时(秒)',
'providers.form.customConfig': '自定义配置',
'providers.form.model.test': '测试连接'
'providers.form.model.test': '测试',
'providers.form.model.test.tips': '测试连接'
};
+35 -6
View File
@@ -1,9 +1,11 @@
import { request } from '@umijs/max';
import { FormData, MaasProviderItem } from '../config/types';
export const MAAS_PROVIDERS_API = '/models';
export const MAAS_PROVIDERS_API = '/model-providers';
export const PROVIDER_MODELS_API = '/models';
export const GET_PROVIDER_MODELS_API = '/get-models';
export const TEST_PROVIDER_MODEL_API = '/test-model';
export async function queryMaasProviders(
params: Global.SearchParams,
@@ -37,14 +39,41 @@ export async function deleteProvider(id: number) {
}
export async function queryProviderModels(
params: { id: string },
params: {
data: {
api_token: string;
config: {
type: string;
};
};
},
options?: any
) {
return request<Global.BaseOption<string>[]>(
`${MAAS_PROVIDERS_API}/${params.id}/models`,
return request<{ data: any[] }>(
`${MAAS_PROVIDERS_API}${GET_PROVIDER_MODELS_API}`,
{
method: 'GET',
method: 'post',
data: params.data,
cancelToken: options?.token
}
);
}
export async function testProviderModel(
params: {
data: {
model_name: string;
api_token: string;
config: {
type: string;
};
};
},
options?: any
) {
return request<any>(`${MAAS_PROVIDERS_API}${TEST_PROVIDER_MODEL_API}`, {
method: 'post',
data: params.data,
cancelToken: options?.token
});
}
@@ -3,7 +3,6 @@ import FormDrawer from '@/pages/_components/form-drawer';
import React, { useRef } from 'react';
import { FormData, MaasProviderItem as ListItem } from '../config/types';
import { maasProviderType } from '../config';
import ProviderForm from '../forms';
type AddModalProps = {
@@ -11,7 +10,6 @@ type AddModalProps = {
action: PageActionType;
open: boolean;
currentData?: ListItem; // Used when action is EDIT
provider: maasProviderType | null;
onOk: (values: FormData) => void;
onCancel: () => void;
};
@@ -19,7 +17,6 @@ const AddProvider: React.FC<AddModalProps> = ({
title,
action,
open,
provider,
currentData,
onOk,
onCancel
@@ -30,7 +27,8 @@ const AddProvider: React.FC<AddModalProps> = ({
form.current?.submit();
};
const handleOk = async (data: FormData) => {
const handleOnFinish = async (data: FormData) => {
console.log('handleOnFinish', data);
onOk({
...data
});
@@ -53,7 +51,7 @@ const AddProvider: React.FC<AddModalProps> = ({
ref={form}
action={action}
currentData={currentData}
onFinish={handleOk}
onFinish={handleOnFinish}
/>
</FormDrawer>
);
@@ -1,5 +1,4 @@
import AutoTooltip from '@/components/auto-tooltip';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import {
CheckCircleOutlined,
CloseCircleOutlined,
@@ -8,56 +7,13 @@ import {
import { Flex, Tag } from 'antd';
import React from 'react';
import { categoryConfig } from '../../_components/model-tag';
import { ProviderModel } from '../config/types';
interface ProviderModelProps {
dataList: any[];
provider: string;
providerId: number;
dataList: ProviderModel[];
}
const models: {
name: string;
status: 'accessible' | 'inaccessible' | 'none';
type: string;
}[] = [
{
name: 'model-1',
status: 'accessible',
type: modelCategoriesMap.llm
},
{
name: 'model-2',
status: 'inaccessible',
type: modelCategoriesMap.embedding
},
{
name: 'model-3',
status: 'none',
type: modelCategoriesMap.image
},
{
name: 'model-4',
status: 'accessible',
type: modelCategoriesMap.llm
},
{
name: 'model-5',
status: 'inaccessible',
type: modelCategoriesMap.embedding
},
{
name: 'model-6model-6model-6model-6model-6model-6model-6',
status: 'none',
type: modelCategoriesMap.image
},
{
name: 'model-7',
status: 'accessible',
type: modelCategoriesMap.llm
}
];
const ProviderModels: React.FC<ProviderModelProps> = () => {
const ProviderModels: React.FC<ProviderModelProps> = ({ dataList }) => {
const iconsMap = {
accessible: <CheckCircleOutlined />,
inaccessible: <CloseCircleOutlined />,
@@ -66,10 +22,14 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
return (
<div style={{ paddingInline: 16 }}>
<Flex gap="8px" wrap="wrap">
{models.map((model) => (
{dataList.map((model) => (
<Tag
key={model.name}
icon={iconsMap[model.status]}
icon={
model.accessible !== null
? iconsMap[model.accessible ? 'accessible' : 'inaccessible']
: iconsMap['none']
}
variant="outlined"
styles={{
root: {
@@ -80,9 +40,9 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
}
}}
color={
model.status === 'accessible'
model.accessible === true
? 'success'
: model.status === 'inaccessible'
: model.accessible === false
? 'error'
: 'warning'
}
@@ -92,7 +52,7 @@ const ProviderModels: React.FC<ProviderModelProps> = () => {
{model.name}
</AutoTooltip>
<span style={{ marginLeft: 8 }}>
{categoryConfig[model.type]?.icon}
{categoryConfig[model.category]?.icon}
</span>
</span>
</Tag>
@@ -1,8 +1,9 @@
import { PageActionType } from '@/config/types';
import { createContext, useContext } from 'react';
import { maasProviderType } from '.';
interface FormContextProps {
providerType?: string;
providerType?: maasProviderType;
action?: PageActionType;
}
-59
View File
@@ -1,59 +0,0 @@
import { MaasProviderItem } from '../config/types';
// mock data for MaasProviderItem 5 items
export const mockDataList: MaasProviderItem[] = [
{
id: 1,
name: 'Doubao',
state: 'Ready',
models: 5,
state_message: '',
provider: 'doubao',
api_tokens: ['token1', 'token2'],
proxy_url: 'http://proxy.example.com',
builtIn: false
},
{
id: 2,
models: 3,
name: 'Qwen',
state: 'inactive',
state_message: 'Provider is inactive',
provider: 'qwen',
proxy_url: 'http://proxy2.example.com',
builtIn: true
},
{
id: 3,
models: 8,
name: 'OpenAI',
state: 'ready',
state_message: '',
proxy_url: null,
api_tokens: ['tokenA'],
provider: 'openai',
builtIn: false
},
{
id: 4,
models: 2,
name: 'Deepseek',
state: 'ready',
state_message: '',
provider: 'deepseek',
proxy_url: null,
api_tokens: ['tokenX', 'tokenY'],
builtIn: false
},
{
id: 5,
models: 4,
name: 'Anthropic',
state: 'inactive',
state_message: 'Provider is inactive',
provider: 'claude',
proxy_url: {},
builtIn: true
}
];
+7 -2
View File
@@ -1,9 +1,14 @@
import { maasProviderType } from '.';
export interface ProviderModel {
name: string;
category: string;
accessible: boolean | null;
}
export interface FormData {
name: string;
description: string;
api_tokens: string[];
models: ProviderModel[];
config: {
type: maasProviderType;
[key: string]: any;
@@ -15,7 +20,7 @@ export interface MaasProviderItem extends FormData {
updated_at: string;
deleted_at: string;
timeout: number;
support_models: unknown[];
models: ProviderModel[];
proxy_url: string;
proxy_timeout: number;
builtin: boolean;
@@ -8,13 +8,16 @@ const AccessToken = () => {
const intl = useIntl();
const { getRuleMessage } = useAppUtils();
const form = Form.useFormInstance<FormData>();
const tokenList = Form.useWatch('api_tokens', form) || [];
const handleOnChange = (values: string[]) => {};
const handleOnChange = (values: string[]) => {
form.setFieldValue('api_tokens', values);
};
return (
<>
<Form.Item
name="tokens"
name="api_tokens"
rules={[
{
required: true,
@@ -29,7 +32,7 @@ const AccessToken = () => {
required={true}
btnText={intl.formatMessage({ id: 'providers.form.tokens.add' })}
label={intl.formatMessage({ id: 'providers.form.tokens.title' })}
dataList={[]}
dataList={tokenList}
onChange={handleOnChange}
></ListInput>
</Form.Item>
@@ -4,14 +4,12 @@ import YamlEditor from '@/pages/_components/yaml-editor';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import React, { forwardRef, useImperativeHandle } from 'react';
import { maasProviderType } from '../config';
import ProxyConfig from './proxy-config';
const AdvanceConfig: React.FC<{
action: PageActionType;
provider?: maasProviderType;
ref?: any;
}> = forwardRef(({ action, provider }, ref) => {
}> = forwardRef(({ action }, ref) => {
const form = Form.useFormInstance();
const intl = useIntl();
const editorRef = React.useRef<any>(null);
+15 -1
View File
@@ -1,5 +1,6 @@
import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select';
import useAppUtils from '@/hooks/use-app-utils';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import ProviderLogo from '../components/provider-logo';
@@ -9,6 +10,7 @@ import { FormData } from '../config/types';
const Basic = () => {
const intl = useIntl();
const form = Form.useFormInstance<FormData>();
const { getRuleMessage } = useAppUtils();
const optionRender = (option: any) => {
return (
@@ -29,9 +31,21 @@ const Basic = () => {
})}
/>
</Form.Item>
<Form.Item<FormData> name={['config', 'type']}>
<Form.Item<FormData>
name={['config', 'type']}
rules={[
{
required: true,
message: getRuleMessage(
'select',
intl.formatMessage({ id: 'providers.table.providerName' })
)
}
]}
>
<SealSelect
showSearch
required
options={maasProviderOptions}
optionRender={optionRender}
labelRender={optionRender}
+11 -17
View File
@@ -13,7 +13,6 @@ import {
useRef,
useState
} from 'react';
import { maasProviderType } from '../config';
import FormContext from '../config/form-context';
import { FormData, MaasProviderItem as ListItem } from '../config/types';
import AccessToken from './access-token';
@@ -24,7 +23,6 @@ import SupportedModels from './supported-models';
interface ProviderFormProps {
ref?: any;
action: PageActionType;
provider?: maasProviderType;
currentData?: ListItem; // Used when action is EDIT
onFinish: (values: FormData) => Promise<void>;
}
@@ -37,7 +35,7 @@ const TABKeysMap = {
};
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const { action, provider, currentData, onFinish } = props;
const { action, currentData, onFinish } = props;
const intl = useIntl();
const [form] = Form.useForm();
const { getScrollElementScrollableHeight } = useWrapperContext();
@@ -83,10 +81,13 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}));
useEffect(() => {
if (action === PageAction.EDIT && currentData) {
if (
(action === PageAction.EDIT || action === PageAction.COPY) &&
currentData
) {
form.setFieldsValue({
...currentData,
models: currentData.models || []
proxy_enabled: !!currentData.proxy_url
});
}
}, [form, currentData, action]);
@@ -104,16 +105,14 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}}
getScrollElementScrollableHeight={getScrollElementScrollableHeight}
>
<FormContext.Provider value={{ providerType: provider, action }}>
<FormContext.Provider value={{ action }}>
<Form
form={form}
onFinish={onFinish}
initialValues={{
proxy_config: {
enabled: false,
url: '',
timeout: 30
}
proxy_enabled: false,
proxy_url: '',
proxy_timeout: 30
}}
>
<Basic />
@@ -133,12 +132,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
key: TABKeysMap.ADVANCED,
label: intl.formatMessage({ id: 'resources.form.advanced' }),
forceRender: true,
children: (
<AdvanceConfig
action={action}
provider={provider}
></AdvanceConfig>
)
children: <AdvanceConfig action={action}></AdvanceConfig>
}
]}
></CollapsePanel>
@@ -0,0 +1,134 @@
import SealSelect from '@/components/seal-form/seal-select';
import { categoryOptions } from '@/pages/llmodels/config';
import {
CheckCircleFilled,
CloseCircleFilled,
LoadingOutlined
} from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { Button, Form, Tooltip } from 'antd';
import React from 'react';
import styled from 'styled-components';
import { FormData, ProviderModel } from '../config/types';
import { useTestProviderModel } from '../hooks/use-query-provider-models';
const SelectWrapper = styled.div`
width: 100%;
display: grid;
grid-template-columns: 265px 160px max-content;
align-items: center;
gap: 8px;
.icon-wrapper {
display: flex;
align-items: center;
height: 100%;
}
`;
interface ModelItemProps {
onOpenChange: (open: boolean) => void;
onChange: (data: ProviderModel) => void;
providerModelList: ProviderModel[];
item: ProviderModel;
loading?: boolean;
}
const ModelItem: React.FC<ModelItemProps> = ({
onOpenChange,
onChange,
loading,
providerModelList,
item
}) => {
const intl = useIntl();
const form = Form.useFormInstance<FormData>();
const { runTestModel, loading: testLoading } = useTestProviderModel();
const handleTestModel = async () => {
const res = await runTestModel({
data: {
model_name: item.name,
api_token: form.getFieldValue('api_tokens')?.[0] || '',
config: {
type: form.getFieldValue(['config', 'type']) || ''
}
}
});
onChange({
...item,
accessible: res.accessible
});
};
const handleOnChange = (value: string) => {
onChange({
...item,
accessible: null,
name: value
});
};
const handleOnCategoryChange = (value: string) => {
onChange({
...item,
category: value
});
};
const renderSuffixIcon = () => {
if (testLoading) {
return <LoadingOutlined />;
}
if (item.accessible === true) {
return (
<CheckCircleFilled
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
/>
);
}
if (item.accessible === false) {
return (
<CloseCircleFilled
style={{ color: 'var(--ant-color-error)', fontSize: 16 }}
/>
);
}
return null;
};
return (
<SelectWrapper>
<SealSelect
loading={loading}
showSearch
onOpenChange={onOpenChange}
suffixIcon={renderSuffixIcon()}
alwaysFocus={true}
value={item.name}
onChange={handleOnChange}
options={providerModelList}
/>
<SealSelect
value={item.category}
onChange={handleOnCategoryChange}
options={categoryOptions}
placeholder={intl.formatMessage({
id: 'models.form.categories'
})}
></SealSelect>
<Tooltip
title={intl.formatMessage({ id: 'providers.form.model.test.tips' })}
>
<Button type="link" size="small" onClick={handleTestModel}>
{testLoading ? (
<LoadingOutlined />
) : (
intl.formatMessage({ id: 'providers.form.model.test' })
)}
</Button>
</Tooltip>
</SelectWrapper>
);
};
export default ModelItem;
+19 -11
View File
@@ -12,36 +12,44 @@ const AdvanceConfig = () => {
const intl = useIntl();
const { action } = useFormContext();
const proxyConfigRef = React.useRef<any>({});
const proxyConfigEnabled = Form.useWatch(['proxy_config', 'enabled'], form);
const proxyConfigEnabled = Form.useWatch('proxy_enabled', form);
const handleSpeculativeEnabledChange = (e: any) => {
const handleProxyEnabledChange = (e: any) => {
if (e.target.checked) {
form.setFieldValue('speculative_config', {
enabled: true,
url: proxyConfigRef.current?.url || '',
timeout: proxyConfigRef.current?.timeout || 30
form.setFieldsValue({
proxy_enabled: true,
proxy_url: proxyConfigRef.current.proxy_url || '',
proxy_timeout: proxyConfigRef.current.proxy_timeout || 30
});
} else {
proxyConfigRef.current = form.getFieldValue('proxy_config');
proxyConfigRef.current = form.getFieldsValue([
'proxy_url',
'proxy_timeout'
]);
form.setFieldsValue({
proxy_enabled: false,
proxy_url: '',
proxy_timeout: 30
});
}
};
return (
<>
<Form.Item
name={['proxy_config', 'enabled']}
name="proxy_enabled"
valuePropName="checked"
style={{ marginBottom: 8 }}
>
<CheckboxField
label={intl.formatMessage({ id: 'providers.form.proxy.enable' })}
onChange={handleSpeculativeEnabledChange}
onChange={handleProxyEnabledChange}
></CheckboxField>
</Form.Item>
{proxyConfigEnabled && (
<>
<Form.Item
name={['proxy_config', 'url']}
name="proxy_url"
rules={[
{
required: true,
@@ -58,7 +66,7 @@ const AdvanceConfig = () => {
placeholder="http://proxy.example.com:8080"
></SealInput.Input>
</Form.Item>
<Form.Item name={['proxy_config', 'timeout']}>
<Form.Item name="proxy_timeout">
<SealInput.Number
label={intl.formatMessage({ id: 'providers.form.proxy.timeout' })}
placeholder="30"
@@ -1,88 +1,79 @@
import ListInput from '@/components/list-input';
import SealSelect from '@/components/seal-form/seal-select';
import { CheckCircleFilled } from '@ant-design/icons';
import MetadataList from '@/components/metadata-list';
import { useIntl } from '@umijs/max';
import { Button, Form } from 'antd';
import styled from 'styled-components';
import { FormData } from '../config/types';
const SelectWrapper = styled.div`
flex: 1;
display: flex;
align-items: center;
gap: 8px;
.icon-wrapper {
display: flex;
align-items: center;
height: 100%;
}
`;
import { Form } from 'antd';
import { FormData, ProviderModel } from '../config/types';
import { useQueryProviderModels } from '../hooks/use-query-provider-models';
import ModelItem from './model-item';
const SupportedModels = () => {
const intl = useIntl();
const { providerModelList, loading, fetchProviderModels } =
useQueryProviderModels();
const form = Form.useFormInstance<FormData>();
const modelList = Form.useWatch('models', form) || [];
const handleTestModel = (data: any) => {
data.loading = true;
setTimeout(() => {
data.loading = false;
}, 1000);
};
const handleOpenChange = async (open: boolean) => {
try {
await form.validateFields(['api_tokens', ['config', 'type']]);
const renderModelItem = (
data: any,
{
onChange,
onBlur
}: { onChange: (value: string) => void; onBlur?: (e: any) => void }
) => {
return (
<SelectWrapper>
<SealSelect
suffixIcon={
<CheckCircleFilled
style={{ color: 'var(--ant-color-success)', fontSize: 16 }}
/>
if (open && providerModelList.length === 0) {
fetchProviderModels({
data: {
api_token: form.getFieldValue('api_tokens')?.[0] || '',
config: {
type: form.getFieldValue(['config', 'type']) || ''
}
}
alwaysFocus={true}
value={data.value}
onChange={onChange}
onBlur={onBlur}
options={[
{ label: 'Model A', value: 'model_a' },
{ label: 'Model B', value: 'model_b' },
{ label: 'Model C', value: 'model_c' }
]}
/>
<Button
type="link"
size="small"
loading={data.loading}
onClick={() => handleTestModel(data)}
>
{intl.formatMessage({ id: 'providers.form.model.test' })}
</Button>
</SelectWrapper>
);
});
}
} catch (error) {}
};
const handleOnChange = (values: string[]) => {};
const updateModelList = (models: ProviderModel[]) => {
form.setFieldsValue({
models: models
});
};
const onAdd = () => {
const newList = [...modelList];
newList.push({ name: '', category: '', accessible: null });
updateModelList(newList);
};
const onDelete = (index: number) => {
const newList = [...modelList];
newList.splice(index, 1);
updateModelList(newList);
};
const onChange = (data: ProviderModel, index: number) => {
const newList = [...modelList];
newList[index] = { ...data };
updateModelList(newList);
};
return (
<>
<Form.Item name="models">
<div data-field="supportedModels"></div>
<ListInput
styles={{
wrapper: {
paddingTop: 14
}
}}
<Form.Item name="models" data-field="supportedModels">
<MetadataList
dataList={modelList}
label={intl.formatMessage({ id: 'providers.table.models' })}
btnText={intl.formatMessage({ id: 'providers.form.models.add' })}
dataList={[]}
renderItem={renderModelItem}
onChange={handleOnChange}
></ListInput>
onAdd={onAdd}
onDelete={onDelete}
>
{(item, index) => (
<ModelItem
key={index}
onOpenChange={handleOpenChange}
onChange={(data) => onChange(data, index)}
loading={loading}
providerModelList={providerModelList}
item={item}
></ModelItem>
)}
</MetadataList>
</Form.Item>
</>
);
@@ -1,7 +1,6 @@
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import { useState } from 'react';
import { maasProviderType } from '../config';
import { MaasProviderItem as ListItem } from '../config/types';
const useCreateProvider = (options: { refresh: () => void }) => {
@@ -10,13 +9,11 @@ const useCreateProvider = (options: { refresh: () => void }) => {
action: PageActionType;
currentData?: ListItem;
title: string;
provider: maasProviderType | null;
}>({
open: false,
action: PageAction.CREATE,
currentData: undefined,
title: '',
provider: null
title: ''
});
const openModal = (action: PageActionType, title: string, row?: ListItem) => {
@@ -25,8 +22,7 @@ const useCreateProvider = (options: { refresh: () => void }) => {
open: true,
title: title,
action,
currentData: row,
provider: row ? row.provider : null
currentData: row
});
};
@@ -35,8 +31,7 @@ const useCreateProvider = (options: { refresh: () => void }) => {
open: false,
action: PageAction.CREATE,
currentData: undefined,
title: '',
provider: null
title: ''
});
};
@@ -10,7 +10,7 @@ import { useMemo } from 'react';
import ProviderLogo from '../components/provider-logo';
import ProviderModels from '../components/provider-models';
import { maasProviderLabelMap, rowActionList } from '../config';
import { MaasProviderItem } from '../config/types';
import { MaasProviderItem, ProviderModel } from '../config/types';
const useProviderColumns = (
handleSelect: (val: string, record: MaasProviderItem) => void,
@@ -41,7 +41,7 @@ const useProviderColumns = (
},
{
title: intl.formatMessage({ id: 'providers.table.providerName' }),
dataIndex: 'provider',
dataIndex: ['config', 'type'],
sorter: tableSorter(2),
span: 4,
minWidth: 160,
@@ -61,8 +61,9 @@ const useProviderColumns = (
dataIndex: 'models',
span: 3,
minWidth: 200,
sorter: tableSorter(3),
render: (value: number) => <ProviderModels></ProviderModels>
render: (value: ProviderModel[]) => (
<ProviderModels dataList={value || []}></ProviderModels>
)
},
{
title: intl.formatMessage({ id: 'common.table.createTime' }),
@@ -0,0 +1,102 @@
import { createAxiosToken } from '@/hooks/use-chunk-request';
import { useRequest } from 'ahooks';
import { message } from 'antd';
import { CancelTokenSource } from 'axios';
import { useEffect, useRef, useState } from 'react';
import { queryProviderModels, testProviderModel } from '../apis';
/**
*
* @returns loading, fetch, dataList
*/
export const useQueryProviderModels = () => {
const axiosTokenRef = useRef<CancelTokenSource | null>(null);
const [providerModelList, setProviderModelList] = useState<any[]>([]);
const {
run: fetchProviderModels,
loading,
cancel
} = useRequest(
async (params: {
data: { api_token: string; config: { type: string } };
}) => {
axiosTokenRef.current?.cancel();
axiosTokenRef.current = createAxiosToken();
return await queryProviderModels(params, {
token: axiosTokenRef.current.token
});
},
{
manual: true,
onSuccess: (response) => {
setProviderModelList(
response.data?.map((item: any) => ({
label: item.id,
value: item.id
})) || []
);
},
onError: () => {
setProviderModelList([]);
}
}
);
useEffect(() => {
return () => {
cancel();
axiosTokenRef.current?.cancel();
};
}, []);
return {
loading,
providerModelList,
fetchProviderModels
};
};
export const useTestProviderModel = () => {
const axiosTokenRef = useRef<CancelTokenSource | null>(null);
const {
runAsync: runTestModel,
loading,
cancel
} = useRequest(
async (params: {
data: { api_token: string; config: { type: string }; model_name: string };
}) => {
axiosTokenRef.current?.cancel();
axiosTokenRef.current = createAxiosToken();
const response = await testProviderModel(params, {
token: axiosTokenRef.current.token
});
return response;
},
{
manual: true,
onSuccess: (response) => {
if (!response?.accessible) {
message.error(response?.error_message || 'Test model failed');
}
},
onError: (error) => {
message.error(error?.message || 'Test model failed');
}
}
);
useEffect(() => {
return () => {
cancel();
axiosTokenRef.current?.cancel();
};
}, []);
return {
loading,
runTestModel
};
};
+13 -16
View File
@@ -7,7 +7,6 @@ import { PageAction } from '@/config';
import { TABLE_SORT_DIRECTIONS } from '@/config/settings';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableFetch from '@/hooks/use-table-fetch';
import useWatchList from '@/hooks/use-watch-list';
import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, message, Table } from 'antd';
@@ -16,16 +15,15 @@ import _ from 'lodash';
import NoResult from '../_components/no-result';
import PageBox from '../_components/page-box';
import {
createProvider,
deleteProvider,
MAAS_PROVIDERS_API,
PROVIDER_MODELS_API,
queryMaasProviders,
updateProvider
} from './apis';
import AddMaasProvider from './components/add-provider-modal';
import ProviderModels from './components/provider-models';
import { maasProviderOptions } from './config';
import { mockDataList } from './config/mock';
import {
FormData,
MaasProviderItem as ListItem,
@@ -50,12 +48,10 @@ const MaasProvider: React.FC = () => {
} = useTableFetch<ListItem>({
fetchAPI: queryMaasProviders,
deleteAPI: deleteProvider,
watch: true,
watch: false,
API: MAAS_PROVIDERS_API,
contentForDelete: 'menu.models.providers'
});
const { watchDataList: allProviderModels } =
useWatchList(PROVIDER_MODELS_API);
const [expandAtom] = useAtom(expandKeysAtom);
const { handleExpandChange, handleExpandAll, expandedRowKeys } =
useExpandedRowKeys(expandAtom);
@@ -83,6 +79,14 @@ const MaasProvider: React.FC = () => {
id: openProviderModalStatus.currentData!.id
});
}
if (
openProviderModalStatus.action === PageAction.CREATE ||
openProviderModalStatus.action === PageAction.COPY
) {
await createProvider({
data: params
});
}
fetchData();
closeProviderModal();
message.success(intl.formatMessage({ id: 'common.message.success' }));
@@ -112,7 +116,7 @@ const MaasProvider: React.FC = () => {
handleDelete({ ...row, name: row.name });
}
if (val === 'copy') {
openProviderModal(PageAction.EDIT, 'Copy Provider', row);
openProviderModal(PageAction.COPY, 'Copy Provider', row);
}
});
@@ -149,13 +153,7 @@ const MaasProvider: React.FC = () => {
list: any,
options: { parent?: any; [key: string]: any }
) => {
return (
<ProviderModels
dataList={list}
provider={options.parent?.provider}
providerId={options.parent?.id}
/>
);
return <ProviderModels dataList={list} />;
};
const renderEmpty = (type?: string) => {
@@ -215,7 +213,7 @@ const MaasProvider: React.FC = () => {
renderChildren={renderChildren}
onTableSort={handleOnSortChange}
showSorterTooltip={false}
dataSource={mockDataList}
dataSource={dataSource.dataList}
loading={dataSource.loading}
loadend={dataSource.loadend}
rowSelection={rowSelection}
@@ -235,7 +233,6 @@ const MaasProvider: React.FC = () => {
</ConfigProvider>
</PageBox>
<AddMaasProvider
provider={openProviderModalStatus.provider}
open={openProviderModalStatus.open}
action={openProviderModalStatus.action}
title={openProviderModalStatus.title}
+3 -3
View File
@@ -1,9 +1,9 @@
import { request } from '@umijs/max';
import { AccessItem, FormData } from '../config/types';
export const ACCESS_API = '/accesses';
export const ACCESS_API = '/model-accesses';
export const ACCESS_POINTS_API = '/models';
export const ACCESS_POINTS_API = '/model-access-endpoints';
export async function queryModelAccesses(
params: Global.SearchParams,
@@ -38,7 +38,7 @@ export async function deleteAccess(id: number) {
export async function queryAccessPoints(params: { id: string }, options?: any) {
return request<Global.BaseOption<string>[]>(
`${ACCESS_API}/${params.id}/points`,
`${ACCESS_API}/${params.id}/access`,
{
method: 'GET',
cancelToken: options?.token
-41
View File
@@ -2,47 +2,6 @@ import icons from '@/components/icon-font/icons';
import { StatusMaps } from '@/config';
import { StatusType } from '@/config/types';
export type maasProviderType =
| 'doubao'
| 'qwen'
| 'openai'
| 'deepseek'
| 'anthropic';
export const maasProviderValueMap = {
Doubao: 'doubao',
Qwen: 'qwen',
OpenAI: 'openai',
Deepseek: 'deepseek',
Anthropic: 'anthropic'
};
export const maasProviderLabelMap = {
[maasProviderValueMap.Doubao]: 'Doubao',
[maasProviderValueMap.Qwen]: 'Qwen',
[maasProviderValueMap.OpenAI]: 'OpenAI',
[maasProviderValueMap.Deepseek]: 'Deepseek',
[maasProviderValueMap.Anthropic]: 'Anthropic'
};
export const providerIconsMap = {
[maasProviderValueMap.Doubao]: 'icon-doubao',
[maasProviderValueMap.Qwen]: 'icon-qwen',
[maasProviderValueMap.OpenAI]: 'icon-openai',
[maasProviderValueMap.Deepseek]: 'icon-deepseek',
[maasProviderValueMap.Anthropic]: 'icon-anthropic'
};
export const maasProviderOptions = Object.entries(maasProviderValueMap).map(
([key, value]) => ({
label: maasProviderLabelMap[value],
value: value,
key: value,
locale: false,
icon: providerIconsMap[value]
})
);
export const ProviderStatusValueMap: Record<string, string> = {
Ready: 'Ready',
InActive: 'Inactive'
-7
View File
@@ -27,7 +27,6 @@ import {
} from './apis';
import AccessPoints from './components/access-points';
import AddAccessModal from './components/add-access-modal';
import { maasProviderOptions } from './config';
import { mockDataList } from './config/mock';
import { FormData, AccessItem as ListItem } from './config/types';
import useAccessColumns from './hooks/use-access-columns';
@@ -186,12 +185,6 @@ const Accesses: React.FC = () => {
handleSearch={handleSearch}
handleDeleteByBatch={handleDeleteBatch}
handleClickPrimary={handleClickDropdown}
actionItems={maasProviderOptions.map((option) => ({
...option,
icon: (
<IconFont type={option.icon as string} style={{ fontSize: 14 }} />
)
}))}
></FilterBar>
<TableContext.Provider
value={{