### What problem does this PR solve? feat: support AWS Bedrock #308 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.9.0
| volcAKMessage: 'Please input your VOLC_ACCESS_KEY', | volcAKMessage: 'Please input your VOLC_ACCESS_KEY', | ||||
| addVolcEngineSK: 'VOLC SECRET_KEY', | addVolcEngineSK: 'VOLC SECRET_KEY', | ||||
| volcSKMessage: 'Please input your SECRET_KEY', | volcSKMessage: 'Please input your SECRET_KEY', | ||||
| bedrockModelNameMessage: 'Please input your model name!', | |||||
| addBedrockEngineAK: 'ACCESS KEY', | |||||
| bedrockAKMessage: 'Please input your ACCESS KEY', | |||||
| addBedrockSK: 'SECRET KEY', | |||||
| bedrockSKMessage: 'Please input your SECRET KEY', | |||||
| bedrockRegion: 'AWS Region', | |||||
| bedrockRegionMessage: 'Please select!', | |||||
| 'us-east-1': 'US East (N. Virginia)', | |||||
| 'us-west-2': 'US West (Oregon)', | |||||
| 'ap-southeast-1': 'Asia Pacific (Singapore)', | |||||
| 'ap-northeast-1': 'Asia Pacific (Tokyo)', | |||||
| 'eu-central-1': 'Europe (Frankfurt)', | |||||
| 'us-gov-west-1': 'AWS GovCloud (US-West)', | |||||
| 'ap-southeast-2': 'Asia Pacific (Sydney)', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: 'Registered!', | registered: 'Registered!', |
| volcAKMessage: '請輸入VOLC_ACCESS_KEY', | volcAKMessage: '請輸入VOLC_ACCESS_KEY', | ||||
| addVolcEngineSK: '火山 SECRET_KEY', | addVolcEngineSK: '火山 SECRET_KEY', | ||||
| volcSKMessage: '請輸入VOLC_SECRET_KEY', | volcSKMessage: '請輸入VOLC_SECRET_KEY', | ||||
| bedrockModelNameMessage: '請輸入名稱!', | |||||
| addBedrockEngineAK: 'ACCESS KEY', | |||||
| bedrockAKMessage: '請輸入 ACCESS KEY', | |||||
| addBedrockSK: 'SECRET KEY', | |||||
| bedrockSKMessage: '請輸入 SECRET KEY', | |||||
| bedrockRegion: 'AWS Region', | |||||
| bedrockRegionMessage: '請選擇!', | |||||
| 'us-east-1': '美國東部 (維吉尼亞北部)', | |||||
| 'us-west-2': '美國西部 (俄勒岡州)', | |||||
| 'ap-southeast-1': '亞太地區 (新加坡)', | |||||
| 'ap-northeast-1': '亞太地區 (東京)', | |||||
| 'eu-central-1': '歐洲 (法蘭克福)', | |||||
| 'us-gov-west-1': 'AWS GovCloud (US-West)', | |||||
| 'ap-southeast-2': '亞太地區 (雪梨)', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: '註冊成功', | registered: '註冊成功', |
| volcAKMessage: '请输入VOLC_ACCESS_KEY', | volcAKMessage: '请输入VOLC_ACCESS_KEY', | ||||
| addVolcEngineSK: '火山 SECRET_KEY', | addVolcEngineSK: '火山 SECRET_KEY', | ||||
| volcSKMessage: '请输入VOLC_SECRET_KEY', | volcSKMessage: '请输入VOLC_SECRET_KEY', | ||||
| bedrockModelNameMessage: '请输入名称!', | |||||
| addBedrockEngineAK: 'ACCESS KEY', | |||||
| bedrockAKMessage: '请输入 ACCESS KEY', | |||||
| addBedrockSK: 'SECRET KEY', | |||||
| bedrockSKMessage: '请输入 SECRET KEY', | |||||
| bedrockRegion: 'AWS Region', | |||||
| bedrockRegionMessage: '请选择!', | |||||
| 'us-east-1': '美国东部 (弗吉尼亚北部)', | |||||
| 'us-west-2': '美国西部 (俄勒冈州)', | |||||
| 'ap-southeast-1': '亚太地区 (新加坡)', | |||||
| 'ap-northeast-1': '亚太地区 (东京)', | |||||
| 'eu-central-1': '欧洲 (法兰克福)', | |||||
| 'us-gov-west-1': 'AWS GovCloud (US-West)', | |||||
| 'ap-southeast-2': '亚太地区 (悉尼)', | |||||
| }, | }, | ||||
| message: { | message: { | ||||
| registered: '注册成功', | registered: '注册成功', |
| import { useTranslate } from '@/hooks/common-hooks'; | |||||
| import { IModalProps } from '@/interfaces/common'; | |||||
| import { IAddLlmRequestBody } from '@/interfaces/request/llm'; | |||||
| import { Flex, Form, Input, Modal, Select, Space } from 'antd'; | |||||
| import { useMemo } from 'react'; | |||||
| import { BedrockRegionList } from '../constant'; | |||||
| type FieldType = IAddLlmRequestBody & { | |||||
| bedrock_ak: string; | |||||
| bedrock_sk: string; | |||||
| bedrock_region: string; | |||||
| }; | |||||
| const { Option } = Select; | |||||
| const BedrockModal = ({ | |||||
| visible, | |||||
| hideModal, | |||||
| onOk, | |||||
| loading, | |||||
| llmFactory, | |||||
| }: IModalProps<IAddLlmRequestBody> & { llmFactory: string }) => { | |||||
| const [form] = Form.useForm<FieldType>(); | |||||
| const { t } = useTranslate('setting'); | |||||
| const options = useMemo( | |||||
| () => BedrockRegionList.map((x) => ({ value: x, label: t(x) })), | |||||
| [t], | |||||
| ); | |||||
| const handleOk = async () => { | |||||
| const values = await form.validateFields(); | |||||
| const data = { | |||||
| ...values, | |||||
| llm_factory: llmFactory, | |||||
| }; | |||||
| onOk?.(data); | |||||
| }; | |||||
| return ( | |||||
| <Modal | |||||
| title={t('addLlmTitle', { name: llmFactory })} | |||||
| open={visible} | |||||
| onOk={handleOk} | |||||
| onCancel={hideModal} | |||||
| okButtonProps={{ loading }} | |||||
| footer={(originNode: React.ReactNode) => { | |||||
| return ( | |||||
| <Flex justify={'space-between'}> | |||||
| <a | |||||
| href="https://console.aws.amazon.com/" | |||||
| target="_blank" | |||||
| rel="noreferrer" | |||||
| > | |||||
| {t('ollamaLink', { name: llmFactory })} | |||||
| </a> | |||||
| <Space>{originNode}</Space> | |||||
| </Flex> | |||||
| ); | |||||
| }} | |||||
| > | |||||
| <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('bedrockModelNameMessage') }]} | |||||
| > | |||||
| <Input placeholder={t('bedrockModelNameMessage')} /> | |||||
| </Form.Item> | |||||
| <Form.Item<FieldType> | |||||
| label={t('addBedrockEngineAK')} | |||||
| name="bedrock_ak" | |||||
| rules={[{ required: true, message: t('bedrockAKMessage') }]} | |||||
| > | |||||
| <Input placeholder={t('bedrockAKMessage')} /> | |||||
| </Form.Item> | |||||
| <Form.Item<FieldType> | |||||
| label={t('addBedrockSK')} | |||||
| name="bedrock_sk" | |||||
| rules={[{ required: true, message: t('bedrockSKMessage') }]} | |||||
| > | |||||
| <Input placeholder={t('bedrockSKMessage')} /> | |||||
| </Form.Item> | |||||
| <Form.Item<FieldType> | |||||
| label={t('bedrockRegion')} | |||||
| name="bedrock_region" | |||||
| rules={[{ required: true, message: t('bedrockRegionMessage') }]} | |||||
| > | |||||
| <Select | |||||
| placeholder={t('bedrockRegionMessage')} | |||||
| options={options} | |||||
| allowClear | |||||
| ></Select> | |||||
| </Form.Item> | |||||
| </Form> | |||||
| </Modal> | |||||
| ); | |||||
| }; | |||||
| export default BedrockModal; |
| // Please lowercase the file name | |||||
| export const IconMap = { | |||||
| 'Tongyi-Qianwen': 'tongyi', | |||||
| Moonshot: 'moonshot', | |||||
| OpenAI: 'openai', | |||||
| 'ZHIPU-AI': 'zhipu', | |||||
| 文心一言: 'wenxin', | |||||
| Ollama: 'ollama', | |||||
| Xinference: 'xinference', | |||||
| DeepSeek: 'deepseek', | |||||
| VolcEngine: 'volc_engine', | |||||
| BaiChuan: 'baichuan', | |||||
| Jina: 'jina', | |||||
| MiniMax: 'chat-minimax', | |||||
| Mistral: 'mistral', | |||||
| 'Azure-OpenAI': 'azure', | |||||
| Bedrock: 'bedrock', | |||||
| Gemini: 'gemini', | |||||
| Groq: 'groq-next', | |||||
| OpenRouter: 'open-router', | |||||
| LocalAI: 'local-ai', | |||||
| StepFun: 'stepfun', | |||||
| }; | |||||
| export const BedrockRegionList = [ | |||||
| 'us-east-1', | |||||
| 'us-west-2', | |||||
| 'ap-southeast-1', | |||||
| 'ap-northeast-1', | |||||
| 'eu-central-1', | |||||
| 'us-gov-west-1', | |||||
| 'ap-southeast-2', | |||||
| ]; |
| export const useSubmitVolcEngine = () => { | export const useSubmitVolcEngine = () => { | ||||
| const loading = useOneNamespaceEffectsLoading('settingModel', ['add_llm']); | const loading = useOneNamespaceEffectsLoading('settingModel', ['add_llm']); | ||||
| const [selectedVolcFactory, setSelectedVolcFactory] = useState<string>(''); | |||||
| const addLlm = useAddLlm(); | const addLlm = useAddLlm(); | ||||
| const { | const { | ||||
| visible: volcAddingVisible, | visible: volcAddingVisible, | ||||
| [hideVolcAddingModal, addLlm], | [hideVolcAddingModal, addLlm], | ||||
| ); | ); | ||||
| const handleShowVolcAddingModal = (llmFactory: string) => { | |||||
| setSelectedVolcFactory(llmFactory); | |||||
| showVolcAddingModal(); | |||||
| }; | |||||
| return { | return { | ||||
| volcAddingLoading: loading, | volcAddingLoading: loading, | ||||
| onVolcAddingOk, | onVolcAddingOk, | ||||
| volcAddingVisible, | volcAddingVisible, | ||||
| hideVolcAddingModal, | hideVolcAddingModal, | ||||
| showVolcAddingModal: handleShowVolcAddingModal, | |||||
| selectedVolcFactory, | |||||
| showVolcAddingModal, | |||||
| }; | |||||
| }; | |||||
| export const useSubmitBedrock = () => { | |||||
| const loading = useOneNamespaceEffectsLoading('settingModel', ['add_llm']); | |||||
| const addLlm = useAddLlm(); | |||||
| const { | |||||
| visible: bedrockAddingVisible, | |||||
| hideModal: hideBedrockAddingModal, | |||||
| showModal: showBedrockAddingModal, | |||||
| } = useSetModalState(); | |||||
| const onBedrockAddingOk = useCallback( | |||||
| async (payload: IAddLlmRequestBody) => { | |||||
| const ret = await addLlm(payload); | |||||
| if (ret === 0) { | |||||
| hideBedrockAddingModal(); | |||||
| } | |||||
| }, | |||||
| [hideBedrockAddingModal, addLlm], | |||||
| ); | |||||
| return { | |||||
| bedrockAddingLoading: loading, | |||||
| onBedrockAddingOk, | |||||
| bedrockAddingVisible, | |||||
| hideBedrockAddingModal, | |||||
| showBedrockAddingModal, | |||||
| }; | }; | ||||
| }; | }; | ||||
| Tooltip, | Tooltip, | ||||
| Typography, | Typography, | ||||
| } from 'antd'; | } from 'antd'; | ||||
| import { useCallback } from 'react'; | |||||
| import { useCallback, useMemo } from 'react'; | |||||
| import SettingTitle from '../components/setting-title'; | import SettingTitle from '../components/setting-title'; | ||||
| import { isLocalLlmFactory } from '../utils'; | import { isLocalLlmFactory } from '../utils'; | ||||
| import ApiKeyModal from './api-key-modal'; | import ApiKeyModal from './api-key-modal'; | ||||
| import BedrockModal from './bedrock-modal'; | |||||
| import { IconMap } from './constant'; | |||||
| import { | import { | ||||
| useHandleDeleteLlm, | useHandleDeleteLlm, | ||||
| useSelectModelProvidersLoading, | useSelectModelProvidersLoading, | ||||
| useSubmitApiKey, | useSubmitApiKey, | ||||
| useSubmitBedrock, | |||||
| useSubmitOllama, | useSubmitOllama, | ||||
| useSubmitSystemModelSetting, | useSubmitSystemModelSetting, | ||||
| useSubmitVolcEngine, | useSubmitVolcEngine, | ||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| import OllamaModal from './ollama-modal'; | import OllamaModal from './ollama-modal'; | ||||
| import SystemModelSettingModal from './system-model-setting-modal'; | import SystemModelSettingModal from './system-model-setting-modal'; | ||||
| import VolcEngineModal from './volcengine-model'; | |||||
| // Please lowercase the file name | |||||
| const IconMap = { | |||||
| 'Tongyi-Qianwen': 'tongyi', | |||||
| Moonshot: 'moonshot', | |||||
| OpenAI: 'openai', | |||||
| 'ZHIPU-AI': 'zhipu', | |||||
| 文心一言: 'wenxin', | |||||
| Ollama: 'ollama', | |||||
| Xinference: 'xinference', | |||||
| DeepSeek: 'deepseek', | |||||
| VolcEngine: 'volc_engine', | |||||
| BaiChuan: 'baichuan', | |||||
| Jina: 'jina', | |||||
| MiniMax: 'chat-minimax', | |||||
| Mistral: 'mistral', | |||||
| 'Azure-OpenAI': 'azure', | |||||
| Bedrock: 'bedrock', | |||||
| Gemini: 'gemini', | |||||
| Groq: 'groq-next', | |||||
| OpenRouter: 'open-router', | |||||
| LocalAI:'local-ai', | |||||
| StepFun:'stepfun' | |||||
| }; | |||||
| import VolcEngineModal from './volcengine-modal'; | |||||
| const LlmIcon = ({ name }: { name: string }) => { | const LlmIcon = ({ name }: { name: string }) => { | ||||
| const icon = IconMap[name as keyof typeof IconMap]; | const icon = IconMap[name as keyof typeof IconMap]; | ||||
| showVolcAddingModal, | showVolcAddingModal, | ||||
| onVolcAddingOk, | onVolcAddingOk, | ||||
| volcAddingLoading, | volcAddingLoading, | ||||
| selectedVolcFactory, | |||||
| } = useSubmitVolcEngine(); | } = useSubmitVolcEngine(); | ||||
| const handleApiKeyClick = useCallback( | |||||
| const { | |||||
| bedrockAddingLoading, | |||||
| onBedrockAddingOk, | |||||
| bedrockAddingVisible, | |||||
| hideBedrockAddingModal, | |||||
| showBedrockAddingModal, | |||||
| } = useSubmitBedrock(); | |||||
| const ModalMap = useMemo( | |||||
| () => ({ | |||||
| Bedrock: showBedrockAddingModal, | |||||
| VolcEngine: showVolcAddingModal, | |||||
| }), | |||||
| [showBedrockAddingModal, showVolcAddingModal], | |||||
| ); | |||||
| const handleAddModel = useCallback( | |||||
| (llmFactory: string) => { | (llmFactory: string) => { | ||||
| if (isLocalLlmFactory(llmFactory)) { | if (isLocalLlmFactory(llmFactory)) { | ||||
| showLlmAddingModal(llmFactory); | showLlmAddingModal(llmFactory); | ||||
| } else if (llmFactory === 'VolcEngine') { | |||||
| showVolcAddingModal('VolcEngine'); | |||||
| } else if (llmFactory in ModalMap) { | |||||
| ModalMap[llmFactory as keyof typeof ModalMap](); | |||||
| } else { | } else { | ||||
| showApiKeyModal({ llm_factory: llmFactory }); | showApiKeyModal({ llm_factory: llmFactory }); | ||||
| } | } | ||||
| }, | }, | ||||
| [showApiKeyModal, showLlmAddingModal, showVolcAddingModal], | |||||
| [showApiKeyModal, showLlmAddingModal, ModalMap], | |||||
| ); | ); | ||||
| const handleAddModel = (llmFactory: string) => () => { | |||||
| if (isLocalLlmFactory(llmFactory)) { | |||||
| showLlmAddingModal(llmFactory); | |||||
| } else if (llmFactory === 'VolcEngine') { | |||||
| showVolcAddingModal('VolcEngine'); | |||||
| } else { | |||||
| handleApiKeyClick(llmFactory); | |||||
| } | |||||
| }; | |||||
| const items: CollapseProps['items'] = [ | const items: CollapseProps['items'] = [ | ||||
| { | { | ||||
| key: '1', | key: '1', | ||||
| grid={{ gutter: 16, column: 1 }} | grid={{ gutter: 16, column: 1 }} | ||||
| dataSource={llmList} | dataSource={llmList} | ||||
| renderItem={(item) => ( | renderItem={(item) => ( | ||||
| <ModelCard item={item} clickApiKey={handleApiKeyClick}></ModelCard> | |||||
| <ModelCard item={item} clickApiKey={handleAddModel}></ModelCard> | |||||
| )} | )} | ||||
| /> | /> | ||||
| ), | ), | ||||
| </Flex> | </Flex> | ||||
| </Flex> | </Flex> | ||||
| <Divider></Divider> | <Divider></Divider> | ||||
| <Button type="link" onClick={handleAddModel(item.name)}> | |||||
| <Button type="link" onClick={() => handleAddModel(item.name)}> | |||||
| {t('addTheModel')} | {t('addTheModel')} | ||||
| </Button> | </Button> | ||||
| </Card> | </Card> | ||||
| hideModal={hideVolcAddingModal} | hideModal={hideVolcAddingModal} | ||||
| onOk={onVolcAddingOk} | onOk={onVolcAddingOk} | ||||
| loading={volcAddingLoading} | loading={volcAddingLoading} | ||||
| llmFactory={selectedVolcFactory} | |||||
| llmFactory={'VolcEngine'} | |||||
| ></VolcEngineModal> | ></VolcEngineModal> | ||||
| <BedrockModal | |||||
| visible={bedrockAddingVisible} | |||||
| hideModal={hideBedrockAddingModal} | |||||
| onOk={onBedrockAddingOk} | |||||
| loading={bedrockAddingLoading} | |||||
| llmFactory={'Bedrock'} | |||||
| ></BedrockModal> | |||||
| </section> | </section> | ||||
| ); | ); | ||||
| }; | }; |