Refactor: Refactoring VolcEngine and Yiyan modal using shadcn. #10427 (#12426)

### What problem does this PR solve?

Refactor: Refactoring VolcEngine and Yiyan modal using shadcn. #10427
### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2026-01-05 09:53:47 +08:00
committed by GitHub
parent 5ebe334a2f
commit cc8a10376a
4 changed files with 205 additions and 216 deletions

View File

@ -3,7 +3,7 @@ export interface IAddLlmRequestBody {
llm_name: string;
model_type: string;
api_base?: string; // chat|embedding|speech2text|image2text
api_key: string | Record<string, any>;
api_key?: string | Record<string, any>;
max_tokens: number;
}

View File

@ -1,19 +0,0 @@
import { Form } from 'antd';
import { useEffect, useState } from 'react';
export const useValidateSubmittable = () => {
const [form] = Form.useForm();
const [submittable, setSubmittable] = useState<boolean>(false);
// Watch all values
const values = Form.useWatch([], form);
useEffect(() => {
form
.validateFields({ validateOnly: true })
.then(() => setSubmittable(true))
.catch(() => setSubmittable(false));
}, [form, values]);
return { submittable, form };
};

View File

@ -1,20 +1,20 @@
import { useTranslate } from '@/hooks/common-hooks';
import {
DynamicForm,
FormFieldConfig,
FormFieldType,
} from '@/components/dynamic-form';
import { Modal } from '@/components/ui/modal/modal';
import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { Flex, Form, Input, InputNumber, Modal, Select, Space } from 'antd';
import omit from 'lodash/omit';
import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header';
type FieldType = IAddLlmRequestBody & {
vision: boolean;
volc_ak: string;
volc_sk: string;
type VolcEngineLlmRequest = IAddLlmRequestBody & {
endpoint_id: string;
ark_api_key: string;
};
const { Option } = Select;
const VolcEngineModal = ({
visible,
hideModal,
@ -22,115 +22,122 @@ const VolcEngineModal = ({
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
const [form] = Form.useForm<FieldType>();
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const fields: FormFieldConfig[] = [
{
name: 'model_type',
label: t('modelType'),
type: FormFieldType.Select,
required: true,
options: [
{ label: 'chat', value: 'chat' },
{ label: 'embedding', value: 'embedding' },
{ label: 'image2text', value: 'image2text' },
],
defaultValue: 'chat',
},
{
name: 'llm_name',
label: t('modelName'),
type: FormFieldType.Text,
required: true,
placeholder: t('volcModelNameMessage'),
},
{
name: 'endpoint_id',
label: t('addEndpointID'),
type: FormFieldType.Text,
required: true,
placeholder: t('endpointIDMessage'),
},
{
name: 'ark_api_key',
label: t('addArkApiKey'),
type: FormFieldType.Text,
required: true,
placeholder: t('ArkApiKeyMessage'),
},
{
name: 'max_tokens',
label: t('maxTokens'),
type: FormFieldType.Number,
required: true,
placeholder: t('maxTokensTip'),
validation: {
min: 0,
},
},
];
const handleOk = async (values?: FieldValues) => {
if (!values) return;
const handleOk = async () => {
const values = await form.validateFields();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
const data = {
...omit(values, ['vision']),
model_type: modelType,
const data: VolcEngineLlmRequest = {
llm_factory: llmFactory,
max_tokens: values.max_tokens,
llm_name: values.llm_name as string,
model_type: modelType,
endpoint_id: values.endpoint_id as string,
ark_api_key: values.ark_api_key as string,
max_tokens: values.max_tokens as number,
};
console.info(data);
onOk?.(data);
await onOk?.(data);
};
return (
<Modal
title={<LLMHeader name={llmFactory} />}
open={visible}
onOk={handleOk}
onCancel={hideModal}
okButtonProps={{ loading }}
footer={(originNode: React.ReactNode) => {
return (
<Flex justify={'space-between'}>
<a
href="https://www.volcengine.com/docs/82379/1302008"
target="_blank"
rel="noreferrer"
>
{t('ollamaLink', { name: llmFactory })}
</a>
<Space>{originNode}</Space>
</Flex>
);
}}
open={visible || false}
onOpenChange={(open) => !open && hideModal?.()}
maskClosable={false}
footer={<div className="p-4"></div>}
>
<Form
name="basic"
style={{ maxWidth: 600 }}
autoComplete="off"
layout={'vertical'}
form={form}
<DynamicForm.Root
fields={fields}
onSubmit={(data) => {
console.log(data);
}}
defaultValues={
{
model_type: 'chat',
vision: false,
} as FieldValues
}
labelClassName="font-normal"
>
<Form.Item<FieldType>
label={t('modelType')}
name="model_type"
initialValue={'chat'}
rules={[{ required: true, message: t('modelTypeMessage') }]}
>
<Select placeholder={t('modelTypeMessage')}>
<Option value="chat">chat</Option>
<Option value="embedding">embedding</Option>
<Option value="image2text">image2text</Option>
</Select>
</Form.Item>
<Form.Item<FieldType>
label={t('modelName')}
name="llm_name"
rules={[{ required: true, message: t('volcModelNameMessage') }]}
>
<Input placeholder={t('volcModelNameMessage')} />
</Form.Item>
<Form.Item<FieldType>
label={t('addEndpointID')}
name="endpoint_id"
rules={[{ required: true, message: t('endpointIDMessage') }]}
>
<Input placeholder={t('endpointIDMessage')} />
</Form.Item>
<Form.Item<FieldType>
label={t('addArkApiKey')}
name="ark_api_key"
rules={[{ required: true, message: t('ArkApiKeyMessage') }]}
>
<Input placeholder={t('ArkApiKeyMessage')} />
</Form.Item>
<Form.Item<FieldType>
label={t('maxTokens')}
name="max_tokens"
rules={[
{ required: true, message: t('maxTokensMessage') },
{
type: 'number',
message: t('maxTokensInvalidMessage'),
},
({}) => ({
validator(_, value) {
if (value < 0) {
return Promise.reject(new Error(t('maxTokensMinMessage')));
}
return Promise.resolve();
},
}),
]}
>
<InputNumber
placeholder={t('maxTokensTip')}
style={{ width: '100%' }}
/>
</Form.Item>
</Form>
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-between w-full py-6 px-6">
<a
href="https://www.volcengine.com/docs/82379/1302008"
target="_blank"
rel="noreferrer"
>
{t('ollamaLink', { name: llmFactory })}
</a>
<div className="flex gap-2">
<DynamicForm.CancelButton
handleCancel={() => {
hideModal?.();
}}
/>
<DynamicForm.SavingButton
submitLoading={loading || false}
buttonText={tc('ok')}
submitFunc={(values: FieldValues) => {
handleOk(values);
}}
/>
</div>
</div>
</DynamicForm.Root>
</Modal>
);
};

View File

@ -1,18 +1,15 @@
import { useTranslate } from '@/hooks/common-hooks';
import {
DynamicForm,
FormFieldConfig,
FormFieldType,
} from '@/components/dynamic-form';
import { Modal } from '@/components/ui/modal/modal';
import { useCommonTranslation, useTranslate } from '@/hooks/common-hooks';
import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { Form, Input, InputNumber, Modal, Select } from 'antd';
import omit from 'lodash/omit';
import { FieldValues } from 'react-hook-form';
import { LLMHeader } from '../../components/llm-header';
type FieldType = IAddLlmRequestBody & {
vision: boolean;
yiyan_ak: string;
yiyan_sk: string;
};
const { Option } = Select;
const YiyanModal = ({
visible,
hideModal,
@ -20,111 +17,115 @@ const YiyanModal = ({
loading,
llmFactory,
}: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => {
const [form] = Form.useForm<FieldType>();
const { t } = useTranslate('setting');
const { t: tc } = useCommonTranslation();
const fields: FormFieldConfig[] = [
{
name: 'model_type',
label: t('modelType'),
type: FormFieldType.Select,
required: true,
options: [
{ label: 'chat', value: 'chat' },
{ label: 'embedding', value: 'embedding' },
{ label: 'rerank', value: 'rerank' },
],
defaultValue: 'chat',
},
{
name: 'llm_name',
label: t('modelName'),
type: FormFieldType.Text,
required: true,
placeholder: t('yiyanModelNameMessage'),
},
{
name: 'yiyan_ak',
label: t('addyiyanAK'),
type: FormFieldType.Text,
required: true,
placeholder: t('yiyanAKMessage'),
},
{
name: 'yiyan_sk',
label: t('addyiyanSK'),
type: FormFieldType.Text,
required: true,
placeholder: t('yiyanSKMessage'),
},
{
name: 'max_tokens',
label: t('maxTokens'),
type: FormFieldType.Number,
required: true,
placeholder: t('maxTokensTip'),
validation: {
min: 0,
},
},
];
const handleOk = async (values?: FieldValues) => {
if (!values) return;
const handleOk = async () => {
const values = await form.validateFields();
const modelType =
values.model_type === 'chat' && values.vision
? 'image2text'
: values.model_type;
const data = {
...omit(values, ['vision']),
model_type: modelType,
const data: IAddLlmRequestBody = {
llm_factory: llmFactory,
max_tokens: values.max_tokens,
llm_name: values.llm_name as string,
model_type: modelType,
api_key: {
yiyan_ak: values.yiyan_ak,
yiyan_sk: values.yiyan_sk,
},
max_tokens: values.max_tokens as number,
};
console.info(data);
onOk?.(data);
};
const handleKeyDown = async (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
await handleOk();
}
await onOk?.(data);
};
return (
<Modal
title={<LLMHeader name={llmFactory} />}
open={visible}
onOk={handleOk}
onCancel={hideModal}
okButtonProps={{ loading }}
confirmLoading={loading}
open={visible || false}
onOpenChange={(open) => !open && hideModal?.()}
maskClosable={false}
footer={<div className="p-4"></div>}
>
<Form
name="basic"
style={{ maxWidth: 600 }}
autoComplete="off"
layout={'vertical'}
form={form}
<DynamicForm.Root
fields={fields}
onSubmit={(data) => {
console.log(data);
}}
defaultValues={
{
model_type: 'chat',
vision: false,
} as FieldValues
}
labelClassName="font-normal"
>
<Form.Item<FieldType>
label={t('modelType')}
name="model_type"
initialValue={'chat'}
rules={[{ required: true, message: t('modelTypeMessage') }]}
>
<Select placeholder={t('modelTypeMessage')}>
<Option value="chat">chat</Option>
<Option value="embedding">embedding</Option>
<Option value="rerank">rerank</Option>
</Select>
</Form.Item>
<Form.Item<FieldType>
label={t('modelName')}
name="llm_name"
rules={[{ required: true, message: t('yiyanModelNameMessage') }]}
>
<Input
placeholder={t('yiyanModelNameMessage')}
onKeyDown={handleKeyDown}
<div className="absolute bottom-0 right-0 left-0 flex items-center justify-end w-full gap-2 py-6 px-6">
<DynamicForm.CancelButton
handleCancel={() => {
hideModal?.();
}}
/>
</Form.Item>
<Form.Item<FieldType>
label={t('addyiyanAK')}
name="yiyan_ak"
rules={[{ required: true, message: t('yiyanAKMessage') }]}
>
<Input placeholder={t('yiyanAKMessage')} onKeyDown={handleKeyDown} />
</Form.Item>
<Form.Item<FieldType>
label={t('addyiyanSK')}
name="yiyan_sk"
rules={[{ required: true, message: t('yiyanSKMessage') }]}
>
<Input placeholder={t('yiyanSKMessage')} onKeyDown={handleKeyDown} />
</Form.Item>
<Form.Item<FieldType>
label={t('maxTokens')}
name="max_tokens"
rules={[
{ required: true, message: t('maxTokensMessage') },
{
type: 'number',
message: t('maxTokensInvalidMessage'),
},
({}) => ({
validator(_, value) {
if (value < 0) {
return Promise.reject(new Error(t('maxTokensMinMessage')));
}
return Promise.resolve();
},
}),
]}
>
<InputNumber
placeholder={t('maxTokensTip')}
style={{ width: '100%' }}
<DynamicForm.SavingButton
submitLoading={loading || false}
buttonText={tc('ok')}
submitFunc={(values: FieldValues) => {
handleOk(values);
}}
/>
</Form.Item>
</Form>
</div>
</DynamicForm.Root>
</Modal>
);
};