feat: add support for ollama #221 (#260)

### What problem does this PR solve?

add support for ollama

Issue link:#221

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2024-04-08 19:13:45 +08:00
committed by GitHub
parent d0a1ffe6e2
commit 265a7a283a
22 changed files with 275 additions and 26 deletions

View File

@@ -46,8 +46,10 @@ const ApiKeyModal = ({
};
useEffect(() => {
form.setFieldValue('api_key', initialValue);
}, [initialValue, form]);
if (visible) {
form.setFieldValue('api_key', initialValue);
}
}, [initialValue, form, visible]);
return (
<Modal

View File

@@ -2,6 +2,7 @@ import { useSetModalState } from '@/hooks/commonHooks';
import {
IApiKeySavingParams,
ISystemModelSettingSavingParams,
useAddLlm,
useFetchLlmList,
useSaveApiKey,
useSaveTenantInfo,
@@ -12,6 +13,7 @@ import {
useFetchTenantInfo,
useSelectTenantInfo,
} from '@/hooks/userSettingHook';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { useCallback, useEffect, useState } from 'react';
type SavingParamsState = Omit<IApiKeySavingParams, 'api_key'>;
@@ -127,3 +129,31 @@ export const useSelectModelProvidersLoading = () => {
return loading;
};
export const useSubmitOllama = () => {
const loading = useOneNamespaceEffectsLoading('settingModel', ['add_llm']);
const addLlm = useAddLlm();
const {
visible: llmAddingVisible,
hideModal: hideLlmAddingModal,
showModal: showLlmAddingModal,
} = useSetModalState();
const onLlmAddingOk = useCallback(
async (payload: IAddLlmRequestBody) => {
const ret = await addLlm(payload);
if (ret === 0) {
hideLlmAddingModal();
}
},
[hideLlmAddingModal, addLlm],
);
return {
llmAddingLoading: loading,
onLlmAddingOk,
llmAddingVisible,
hideLlmAddingModal,
showLlmAddingModal,
};
};

View File

@@ -1,15 +1,11 @@
import { ReactComponent as MoreModelIcon } from '@/assets/svg/more-model.svg';
import SvgIcon from '@/components/svg-icon';
import { useSetModalState, useTranslate } from '@/hooks/commonHooks';
import {
LlmItem,
useFetchLlmFactoryListOnMount,
useFetchMyLlmListOnMount,
} from '@/hooks/llmHooks';
import { ReactComponent as MoonshotIcon } from '@/icons/moonshot.svg';
import { ReactComponent as OpenAiIcon } from '@/icons/openai.svg';
import { ReactComponent as TongYiIcon } from '@/icons/tongyi.svg';
import { ReactComponent as WenXinIcon } from '@/icons/wenxin.svg';
import { ReactComponent as ZhiPuIcon } from '@/icons/zhipu.svg';
import { SettingOutlined, UserOutlined } from '@ant-design/icons';
import {
Avatar,
@@ -33,24 +29,27 @@ import ApiKeyModal from './api-key-modal';
import {
useSelectModelProvidersLoading,
useSubmitApiKey,
useSubmitOllama,
useSubmitSystemModelSetting,
} from './hooks';
import styles from './index.less';
import OllamaModal from './ollama-modal';
import SystemModelSettingModal from './system-model-setting-modal';
import styles from './index.less';
const IconMap = {
'Tongyi-Qianwen': TongYiIcon,
Moonshot: MoonshotIcon,
OpenAI: OpenAiIcon,
'ZHIPU-AI': ZhiPuIcon,
文心一言: WenXinIcon,
'Tongyi-Qianwen': 'tongyi',
Moonshot: 'moonshot',
OpenAI: 'openai',
'ZHIPU-AI': 'zhipu',
: 'wenxin',
Ollama: 'ollama',
};
const LlmIcon = ({ name }: { name: string }) => {
const Icon = IconMap[name as keyof typeof IconMap];
return Icon ? (
<Icon width={48} height={48}></Icon>
const icon = IconMap[name as keyof typeof IconMap];
return icon ? (
<SvgIcon name={`llm/${icon}`} width={48} height={48}></SvgIcon>
) : (
<Avatar shape="square" size="large" icon={<UserOutlined />} />
);
@@ -90,7 +89,7 @@ const ModelCard = ({ item, clickApiKey }: IModelCardProps) => {
<Col span={12} className={styles.factoryOperationWrapper}>
<Space size={'middle'}>
<Button onClick={handleApiKeyClick}>
API-Key
{item.name === 'Ollama' ? t('addTheModel') : 'API-Key'}
<SettingOutlined />
</Button>
<Button onClick={handleShowMoreClick}>
@@ -142,16 +141,31 @@ const UserSettingModel = () => {
showSystemSettingModal,
} = useSubmitSystemModelSetting();
const { t } = useTranslate('setting');
const {
llmAddingVisible,
hideLlmAddingModal,
showLlmAddingModal,
onLlmAddingOk,
llmAddingLoading,
} = useSubmitOllama();
const handleApiKeyClick = useCallback(
(llmFactory: string) => {
showApiKeyModal({ llm_factory: llmFactory });
if (llmFactory === 'Ollama') {
showLlmAddingModal();
} else {
showApiKeyModal({ llm_factory: llmFactory });
}
},
[showApiKeyModal],
[showApiKeyModal, showLlmAddingModal],
);
const handleAddModel = (llmFactory: string) => () => {
handleApiKeyClick(llmFactory);
if (llmFactory === 'Ollama') {
showLlmAddingModal();
} else {
handleApiKeyClick(llmFactory);
}
};
const items: CollapseProps['items'] = [
@@ -216,7 +230,7 @@ const UserSettingModel = () => {
clickButton={showSystemSettingModal}
></SettingTitle>
<Divider></Divider>
<Collapse defaultActiveKey={['1']} ghost items={items} />
<Collapse defaultActiveKey={['1', '2']} ghost items={items} />
</section>
</Spin>
<ApiKeyModal
@@ -233,6 +247,12 @@ const UserSettingModel = () => {
hideModal={hideSystemSettingModal}
loading={saveSystemModelSettingLoading}
></SystemModelSettingModal>
<OllamaModal
visible={llmAddingVisible}
hideModal={hideLlmAddingModal}
onOk={onLlmAddingOk}
loading={llmAddingLoading}
></OllamaModal>
</>
);
};

View File

@@ -0,0 +1,96 @@
import { useTranslate } from '@/hooks/commonHooks';
import { IModalProps } from '@/interfaces/common';
import { IAddLlmRequestBody } from '@/interfaces/request/llm';
import { Form, Input, Modal, Select, Switch } from 'antd';
import omit from 'lodash/omit';
type FieldType = IAddLlmRequestBody & { vision: boolean };
const { Option } = Select;
const OllamaModal = ({
visible,
hideModal,
onOk,
loading,
}: IModalProps<IAddLlmRequestBody>) => {
const [form] = Form.useForm<FieldType>();
const { t } = useTranslate('setting');
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,
llm_factory: 'Ollama',
};
console.info(data);
onOk?.(data);
};
return (
<Modal
title={t('addLlmTitle')}
open={visible}
onOk={handleOk}
onCancel={hideModal}
okButtonProps={{ loading }}
>
<Form
name="basic"
style={{ maxWidth: 600 }}
autoComplete="off"
layout={'vertical'}
form={form}
>
<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>
</Select>
</Form.Item>
<Form.Item<FieldType>
label={t('modelName')}
name="llm_name"
rules={[{ required: true, message: t('modelNameMessage') }]}
>
<Input placeholder={t('modelNameMessage')} />
</Form.Item>
<Form.Item<FieldType>
label={t('addLlmBaseUrl')}
name="api_base"
rules={[{ required: true, message: t('baseUrlNameMessage') }]}
>
<Input placeholder={t('baseUrlNameMessage')} />
</Form.Item>
<Form.Item noStyle dependencies={['model_type']}>
{({ getFieldValue }) =>
getFieldValue('model_type') === 'chat' && (
<Form.Item
label={t('vision')}
valuePropName="checked"
name={'vision'}
>
<Switch />
</Form.Item>
)
}
</Form.Item>
</Form>
</Modal>
);
};
export default OllamaModal;

View File

@@ -30,8 +30,10 @@ const SystemModelSettingModal = ({
};
useEffect(() => {
form.setFieldsValue(initialValues);
}, [form, initialValues]);
if (visible) {
form.setFieldsValue(initialValues);
}
}, [form, initialValues, visible]);
const onFormLayoutChange = () => {};