feat: add retriever rank fe (#1557)

Co-authored-by: StyleZhang <jasonapring2015@outlook.com>
This commit is contained in:
Joel
2023-11-18 11:53:35 +08:00
committed by GitHub
parent e017eff5e4
commit 888e8c6dac
80 changed files with 2757 additions and 467 deletions

View File

@@ -0,0 +1,53 @@
import { ProviderEnum } from '../declarations'
import type { ProviderConfig } from '../declarations'
import { Cohere, CohereText } from '@/app/components/base/icons/src/public/llm'
const config: ProviderConfig = {
selector: {
name: {
'en': 'cohere',
'zh-Hans': 'cohere',
},
icon: <Cohere className='w-full h-full' />,
},
item: {
key: ProviderEnum.cohere,
titleIcon: {
'en': <CohereText className='w-[120px] h-6' />,
'zh-Hans': <CohereText className='w-[120px] h-6' />,
},
},
modal: {
key: ProviderEnum.cohere,
title: {
'en': 'cohere',
'zh-Hans': 'cohere',
},
icon: <Cohere className='w-6 h-6' />,
link: {
href: 'https://dashboard.cohere.com/api-keys',
label: {
'en': 'Get your API key from cohere',
'zh-Hans': '从 cohere 获取 API Key',
},
},
validateKeys: ['api_key'],
fields: [
{
type: 'text',
key: 'api_key',
required: true,
label: {
'en': 'API Key',
'zh-Hans': 'API Key',
},
placeholder: {
'en': 'Enter your API key here',
'zh-Hans': '在此输入您的 API Key',
},
},
],
},
}
export default config

View File

@@ -13,6 +13,7 @@ import openllm from './openllm'
import localai from './localai'
import zhipuai from './zhipuai'
import baichuan from './baichuan'
import cohere from './cohere'
export default {
openai,
@@ -30,4 +31,5 @@ export default {
localai,
zhipuai,
baichuan,
cohere,
}

View File

@@ -45,6 +45,7 @@ export enum ProviderEnum {
'localai' = 'localai',
'zhipuai' = 'zhipuai',
'baichuan' = 'baichuan',
'cohere' = 'cohere',
}
export type ProviderConfigItem = {
@@ -67,6 +68,7 @@ export enum ModelType {
textGeneration = 'text-generation',
embeddings = 'embeddings',
speech2text = 'speech2text',
reranking = 'reranking',
}
export enum ModelFeature {

View File

@@ -3,33 +3,28 @@ import useSWR from 'swr'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import type {
BackendModel,
FormValue,
ProviderConfigModal,
ProviderEnum,
} from './declarations'
import ModelSelector from './model-selector'
import ModelCard from './model-card'
import ModelItem from './model-item'
import ModelModal from './model-modal'
import SystemModel from './system-model'
import config from './configs'
import { ConfigurableProviders } from './utils'
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
import {
changeModelProviderPriority,
deleteModelProvider,
deleteModelProviderModel,
fetchDefaultModal,
fetchModelProviders,
setModelProvider,
updateDefaultModel,
} from '@/service/common'
import { useToastContext } from '@/app/components/base/toast'
import Confirm from '@/app/components/base/confirm/common'
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useProviderContext } from '@/context/provider-context'
import Tooltip from '@/app/components/base/tooltip'
import I18n from '@/context/i18n'
const MODEL_CARD_LIST = [
@@ -37,13 +32,6 @@ const MODEL_CARD_LIST = [
config.anthropic,
]
const titleClassName = `
flex items-center h-9 text-sm font-medium text-gray-900
`
const tipClassName = `
ml-0.5 w-[14px] h-[14px] text-gray-400
`
type DeleteModel = {
model_name: string
model_type: string
@@ -54,13 +42,8 @@ const ModelPage = () => {
const { locale } = useContext(I18n)
const {
updateModelList,
embeddingsDefaultModel,
mutateEmbeddingsDefaultModel,
speech2textDefaultModel,
mutateSpeech2textDefaultModel,
} = useProviderContext()
const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
const { data: textGenerationDefaultModel, mutate: mutateTextGenerationDefaultModel } = useSWR('/workspaces/current/default-model?model_type=text-generation', fetchDefaultModal)
const [showModal, setShowModal] = useState(false)
const { notify } = useToastContext()
const { eventEmitter } = useEventEmitterContextContext()
@@ -76,6 +59,7 @@ const ModelPage = () => {
config.azure_openai,
config.replicate,
config.huggingface_hub,
config.cohere,
config.zhipuai,
config.baichuan,
config.spark,
@@ -91,6 +75,7 @@ const ModelPage = () => {
else {
modelList = [
config.huggingface_hub,
config.cohere,
config.zhipuai,
config.spark,
config.baichuan,
@@ -127,6 +112,7 @@ const ModelPage = () => {
updateModelList(ModelType.textGeneration)
updateModelList(ModelType.embeddings)
updateModelList(ModelType.speech2text)
updateModelList(ModelType.reranking)
mutateProviders()
}
const handleSave = async (originValue?: FormValue) => {
@@ -210,95 +196,12 @@ const ModelPage = () => {
}
}
const mutateDefaultModel = (type: ModelType) => {
if (type === ModelType.textGeneration)
mutateTextGenerationDefaultModel()
if (type === ModelType.embeddings)
mutateEmbeddingsDefaultModel()
if (type === ModelType.speech2text)
mutateSpeech2textDefaultModel()
}
const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => {
const res = await updateDefaultModel({
url: '/workspaces/current/default-model',
body: {
model_type: type,
provider_name: v.model_provider.provider_name,
model_name: v.model_name,
},
})
if (res.result === 'success') {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutateDefaultModel(type)
}
}
return (
<div className='relative pt-1 -mt-2'>
<div className='grid grid-cols-3 gap-4 mb-5'>
<div className='w-full'>
<div className={titleClassName}>
{t('common.modelProvider.systemReasoningModel.key')}
<Tooltip
selector='model-page-system-reasoning-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
}
>
<HelpCircle className={tipClassName} />
</Tooltip>
</div>
<div>
<ModelSelector
value={textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name }}
modelType={ModelType.textGeneration}
onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
/>
</div>
</div>
<div className='w-full'>
<div className={titleClassName}>
{t('common.modelProvider.embeddingModel.key')}
<Tooltip
selector='model-page-system-embedding-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
}
>
<HelpCircle className={tipClassName} />
</Tooltip>
</div>
<div>
<ModelSelector
value={embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name }}
modelType={ModelType.embeddings}
onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
/>
</div>
</div>
<div className='w-full'>
<div className={titleClassName}>
{t('common.modelProvider.speechToTextModel.key')}
<Tooltip
selector='model-page-system-speechToText-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
}
>
<HelpCircle className={tipClassName} />
</Tooltip>
</div>
<div>
<ModelSelector
value={speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name }}
modelType={ModelType.speech2text}
onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
/>
</div>
</div>
<div className='flex items-center justify-between mb-2 h-8'>
<div className='text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
<SystemModel />
</div>
<div className='mb-5 h-[0.5px] bg-gray-100' />
<div className='mb-3 text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
<div className='grid grid-cols-2 gap-4 mb-6'>
{
MODEL_CARD_LIST.map((model, index) => (

View File

@@ -34,6 +34,7 @@ type Props = {
popClassName?: string
readonly?: boolean
triggerIconSmall?: boolean
whenEmptyGoToSetting?: boolean
}
type ModelOption = {
@@ -57,10 +58,17 @@ const ModelSelector: FC<Props> = ({
popClassName,
readonly,
triggerIconSmall,
whenEmptyGoToSetting,
}) => {
const { t } = useTranslation()
const { setShowAccountSettingModal } = useModalContext()
const { textGenerationModelList, embeddingsModelList, speech2textModelList, agentThoughtModelList } = useProviderContext()
const {
textGenerationModelList,
embeddingsModelList,
speech2textModelList,
rerankModelList,
agentThoughtModelList,
} = useProviderContext()
const [search, setSearch] = useState('')
const modelList = supportAgentThought
? agentThoughtModelList
@@ -68,6 +76,7 @@ const ModelSelector: FC<Props> = ({
[ModelType.textGeneration]: textGenerationModelList,
[ModelType.embeddings]: embeddingsModelList,
[ModelType.speech2text]: speech2textModelList,
[ModelType.reranking]: rerankModelList,
})[modelType]
const currModel = modelList.find(item => item.model_name === value?.modelName && item.model_provider.provider_name === value.providerName)
const allModelNames = (() => {
@@ -116,7 +125,7 @@ const ModelSelector: FC<Props> = ({
return (
<div className=''>
<Popover className='relative'>
<Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
<Popover.Button className={cn('flex items-center px-2.5 w-full h-9 rounded-lg', readonly ? '!cursor-auto bg-gray-100 opacity-50' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
{
({ open }) => (
<>
@@ -130,7 +139,7 @@ const ModelSelector: FC<Props> = ({
providerName={value.providerName}
/>
<div className='mr-1.5 grow flex items-center text-left text-sm text-gray-900 truncate'>
<ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name} />
<ModelName modelId={value.modelName} modelDisplayName={currModel?.model_display_name || value.modelName} />
{isShowModelModeType && (
<ModelModeTypeLabel className='ml-2' type={currModel?.model_mode as ModelModeType} />
)}
@@ -237,7 +246,22 @@ const ModelSelector: FC<Props> = ({
return null
})
}
{(search && filteredModelList.length === 0) && (
{
whenEmptyGoToSetting && modelList.length === 0 && (
<div className='pt-6'>
<div className='flex items-center justify-center mx-auto mb-2 w-12 h-12 rounded-[10px] border border-[#EAECF5]'>
<CubeOutline className='w-6 h-6 text-gray-500' />
</div>
<div className='mb-1 text-center text-[13px] font-medium text-gray-500'>
{t('common.modelProvider.selector.emptyTip')}
</div>
<div className='mb-6 text-center text-xs text-primary-500'>
<span onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('common.modelProvider.selector.emptySetting')}</span>
</div>
</div>
)
}
{modelList.length !== 0 && (search && filteredModelList.length === 0) && (
<div className='px-3 pt-1.5 h-[30px] text-center text-xs text-gray-500'>{t('common.modelProvider.noModelFound', { model: search })}</div>
)}

View File

@@ -0,0 +1,205 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import ModelSelector from '../model-selector'
import type {
BackendModel, ProviderEnum,
} from '../declarations'
import Tooltip from '@/app/components/base/tooltip'
import { HelpCircle, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import { useProviderContext } from '@/context/provider-context'
import { updateDefaultModel } from '@/service/common'
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
import { useToastContext } from '@/app/components/base/toast'
import Button from '@/app/components/base/button'
const SystemModel = () => {
const { t } = useTranslation()
const {
textGenerationDefaultModel,
mutateTextGenerationDefaultModel,
embeddingsDefaultModel,
mutateEmbeddingsDefaultModel,
speech2textDefaultModel,
mutateSpeech2textDefaultModel,
rerankDefaultModel,
mutateRerankDefaultModel,
} = useProviderContext()
const { notify } = useToastContext()
const [open, setOpen] = useState(false)
const [selectedModel, setSelectedModel] = useState<Record<ModelType, { providerName: ProviderEnum; modelName: string } | undefined>>({
[ModelType.textGeneration]: textGenerationDefaultModel && { providerName: textGenerationDefaultModel.model_provider.provider_name, modelName: textGenerationDefaultModel.model_name },
[ModelType.embeddings]: embeddingsDefaultModel && { providerName: embeddingsDefaultModel.model_provider.provider_name, modelName: embeddingsDefaultModel.model_name },
[ModelType.speech2text]: speech2textDefaultModel && { providerName: speech2textDefaultModel.model_provider.provider_name, modelName: speech2textDefaultModel.model_name },
[ModelType.reranking]: rerankDefaultModel && { providerName: rerankDefaultModel.model_provider.provider_name, modelName: rerankDefaultModel.model_name },
})
const mutateDefaultModel = (types: ModelType[]) => {
types.forEach((type) => {
if (type === ModelType.textGeneration)
mutateTextGenerationDefaultModel()
if (type === ModelType.embeddings)
mutateEmbeddingsDefaultModel()
if (type === ModelType.speech2text)
mutateSpeech2textDefaultModel()
if (type === ModelType.reranking)
mutateRerankDefaultModel()
})
}
const handleChangeDefaultModel = async (type: ModelType, v: BackendModel) => {
setSelectedModel({
...selectedModel,
[type]: {
providerName: v.model_provider.provider_name,
modelName: v.model_name,
},
})
}
const handleSave = async () => {
const kesArray = Object.keys(selectedModel) as ModelType[]
const res = await updateDefaultModel({
url: '/workspaces/current/default-model',
body: {
model_settings: kesArray.map((key) => {
return {
model_type: key,
provider_name: selectedModel?.[key]?.providerName,
model_name: selectedModel?.[key]?.modelName,
}
}),
},
})
if (res.result === 'success') {
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
mutateDefaultModel(kesArray)
}
}
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-end'
offset={{
mainAxis: 4,
crossAxis: 8,
}}
>
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)}>
<div className={`
flex items-center px-2 h-6 text-xs text-gray-700 cursor-pointer rounded-md border-[0.5px] border-gray-200 shadow-xs
hover:bg-gray-100 hover:shadow-none
${open && 'bg-gray-100 shadow-none'}
`}>
<Settings01 className='mr-1 w-3 h-3 text-gray-500' />
{t('common.modelProvider.systemModelSettings')}
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-50'>
<div className='pt-4 w-[360px] rounded-xl border-[0.5px] border-black/5 bg-white shadow-xl'>
<div className='px-6 py-1'>
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
{t('common.modelProvider.systemReasoningModel.key')}
<Tooltip
selector='model-page-system-reasoning-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.systemReasoningModel.tip')}</div>
}
>
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
</Tooltip>
</div>
<div>
<ModelSelector
value={selectedModel[ModelType.textGeneration]}
modelType={ModelType.textGeneration}
onChange={v => handleChangeDefaultModel(ModelType.textGeneration, v)}
/>
</div>
</div>
<div className='px-6 py-1'>
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
{t('common.modelProvider.embeddingModel.key')}
<Tooltip
selector='model-page-system-embedding-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.embeddingModel.tip')}</div>
}
>
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
</Tooltip>
</div>
<div>
<ModelSelector
value={selectedModel[ModelType.embeddings]}
modelType={ModelType.embeddings}
onChange={v => handleChangeDefaultModel(ModelType.embeddings, v)}
/>
</div>
</div>
<div className='px-6 py-1'>
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
{t('common.modelProvider.rerankModel.key')}
<Tooltip
selector='model-page-system-rerankModel-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.rerankModel.tip')}</div>
}
>
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
</Tooltip>
</div>
<div>
<ModelSelector
value={selectedModel[ModelType.reranking]}
modelType={ModelType.reranking}
onChange={v => handleChangeDefaultModel(ModelType.reranking, v)}
/>
</div>
</div>
<div className='px-6 py-1'>
<div className='flex items-center h-8 text-[13px] font-medium text-gray-900'>
{t('common.modelProvider.speechToTextModel.key')}
<Tooltip
selector='model-page-system-speechToText-model-tip'
htmlContent={
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.speechToTextModel.tip')}</div>
}
>
<HelpCircle className='ml-0.5 w-[14px] h-[14px] text-gray-400' />
</Tooltip>
</div>
<div>
<ModelSelector
value={selectedModel[ModelType.speech2text]}
modelType={ModelType.speech2text}
onChange={v => handleChangeDefaultModel(ModelType.speech2text, v)}
/>
</div>
</div>
<div className='flex items-center justify-end px-6 py-4'>
<Button
className='mr-2 !h-8 !text-[13px]'
onClick={() => setOpen(false)}
>
{t('common.operation.cancel')}
</Button>
<Button
type='primary'
className='!h-8 !text-[13px]'
onClick={handleSave}
>
{t('common.operation.save')}
</Button>
</div>
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default SystemModel