feat: frontend multi models support (#804)
Co-authored-by: StyleZhang <jasonapring2015@outlook.com> Co-authored-by: Joel <iamjoel007@gmail.com>
This commit is contained in:
@@ -5,14 +5,17 @@ import cn from 'classnames'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import s from './style.module.css'
|
||||
import Config from '@/app/components/explore/universal-chat/config'
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
|
||||
type Props = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
plugins: Record<string, boolean>
|
||||
dataSets: any[]
|
||||
}
|
||||
const ConfigViewPanel: FC<Props> = ({
|
||||
modelId,
|
||||
providerName,
|
||||
plugins,
|
||||
dataSets,
|
||||
}) => {
|
||||
@@ -23,6 +26,7 @@ const ConfigViewPanel: FC<Props> = ({
|
||||
<Config
|
||||
readonly
|
||||
modelId={modelId}
|
||||
providerName={providerName}
|
||||
plugins={plugins}
|
||||
dataSets={dataSets}
|
||||
/>
|
||||
|
||||
@@ -7,9 +7,11 @@ import s from './style.module.css'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import { Google, WebReader, Wikipedia } from '@/app/components/base/icons/src/public/plugins'
|
||||
import ConfigDetail from '@/app/components/explore/universal-chat/config-view/detail'
|
||||
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
||||
export type ISummaryProps = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
plugins: Record<string, boolean>
|
||||
dataSets: any[]
|
||||
}
|
||||
@@ -40,6 +42,7 @@ const getPlugIcon = (pluginId: string) => {
|
||||
|
||||
const Summary: FC<ISummaryProps> = ({
|
||||
modelId,
|
||||
providerName,
|
||||
plugins,
|
||||
dataSets,
|
||||
}) => {
|
||||
@@ -54,8 +57,8 @@ const Summary: FC<ISummaryProps> = ({
|
||||
return (
|
||||
<div ref={configContentRef} className='relative'>
|
||||
<div onClick={toggleShowConfig} className={cn(getColorInfo(modelId), 'flex items-center px-1 h-8 rounded-lg border cursor-pointer')}>
|
||||
<ModelIcon modelId={modelId} className='!w-6 !h-6' />
|
||||
<div className='ml-2 text-[13px] font-medium text-gray-900'>{modelId}</div>
|
||||
<ModelIcon providerName={providerName} modelId={modelId} className='!w-6 !h-6' />
|
||||
<div className='ml-2 text-[13px] font-medium text-gray-900'><ModelName modelId={modelId} /></div>
|
||||
{
|
||||
pluginIds.length > 0 && (
|
||||
<div className='ml-1.5 flex items-center'>
|
||||
@@ -75,7 +78,10 @@ const Summary: FC<ISummaryProps> = ({
|
||||
</div>
|
||||
{isShowConfig && (
|
||||
<ConfigDetail
|
||||
modelId={modelId} plugins={plugins} dataSets={dataSets}
|
||||
modelId={modelId}
|
||||
providerName={providerName}
|
||||
plugins={plugins}
|
||||
dataSets={dataSets}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -4,12 +4,14 @@ import React from 'react'
|
||||
import ModelConfig from './model-config'
|
||||
import DataConfig from './data-config'
|
||||
import PluginConfig from './plugins-config'
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
|
||||
export type IConfigProps = {
|
||||
className?: string
|
||||
readonly?: boolean
|
||||
modelId: string
|
||||
onModelChange?: (modelId: string) => void
|
||||
providerName: ProviderEnum
|
||||
onModelChange?: (modelId: string, providerName: ProviderEnum) => void
|
||||
plugins: Record<string, boolean>
|
||||
onPluginChange?: (key: string, value: boolean) => void
|
||||
dataSets: any[]
|
||||
@@ -20,6 +22,7 @@ const Config: FC<IConfigProps> = ({
|
||||
className,
|
||||
readonly,
|
||||
modelId,
|
||||
providerName,
|
||||
onModelChange,
|
||||
plugins,
|
||||
onPluginChange,
|
||||
@@ -31,6 +34,7 @@ const Config: FC<IConfigProps> = ({
|
||||
<ModelConfig
|
||||
readonly={readonly}
|
||||
modelId={modelId}
|
||||
providerName={providerName}
|
||||
onChange={onModelChange}
|
||||
/>
|
||||
<PluginConfig
|
||||
|
||||
@@ -1,60 +1,41 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import cn from 'classnames'
|
||||
import { useBoolean, useClickAway } from 'ahooks'
|
||||
import { ChevronDownIcon } from '@heroicons/react/24/outline'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import { UNIVERSAL_CHAT_MODEL_LIST as MODEL_LIST } from '@/config'
|
||||
import { Checked as CheckedIcon } from '@/app/components/base/icons/src/public/model'
|
||||
import { ModelType, type ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
export type IModelConfigProps = {
|
||||
modelId: string
|
||||
onChange?: (model: string) => void
|
||||
providerName: ProviderEnum
|
||||
onChange?: (modelId: string, providerName: ProviderEnum) => void
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
const ModelConfig: FC<IModelConfigProps> = ({
|
||||
modelId,
|
||||
providerName,
|
||||
onChange,
|
||||
readonly,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const currModel = MODEL_LIST.find(item => item.id === modelId)
|
||||
const [isShowOption, { setFalse: hideOption, toggle: toogleOption }] = useBoolean(false)
|
||||
const triggerRef = React.useRef(null)
|
||||
useClickAway(() => {
|
||||
hideOption()
|
||||
}, triggerRef)
|
||||
|
||||
return (
|
||||
<div className='flex items-center justify-between h-[52px] px-3 rounded-xl bg-gray-50'>
|
||||
<div className='text-sm font-semibold text-gray-800'>{t('explore.universalChat.model')}</div>
|
||||
<div className="relative z-10">
|
||||
<div
|
||||
ref={triggerRef}
|
||||
onClick={() => !readonly && toogleOption()}
|
||||
className={cn(
|
||||
readonly ? 'cursor-not-allowed' : 'cursor-pointer', 'flex items-center h-9 px-3 space-x-2 rounded-lg',
|
||||
isShowOption && 'bg-gray-100',
|
||||
)}>
|
||||
<ModelIcon modelId={currModel?.id as string} />
|
||||
<div className="text-sm gray-900">{currModel?.name}</div>
|
||||
{!readonly && <ChevronDownIcon className={cn(isShowOption && 'rotate-180', 'w-[14px] h-[14px] text-gray-500')} />}
|
||||
</div>
|
||||
{isShowOption && (
|
||||
<div className={cn('absolute top-10 right-0 bg-white rounded-lg shadow')}>
|
||||
{MODEL_LIST.map(item => (
|
||||
<div key={item.id} onClick={() => onChange?.(item.id)} className="w-[232px] flex items-center h-9 px-4 rounded-lg cursor-pointer hover:bg-gray-100">
|
||||
<ModelIcon className='shrink-0 mr-2' modelId={item?.id} />
|
||||
<div className="text-sm gray-900 whitespace-nowrap">{item.name}</div>
|
||||
{(item.id === currModel?.id) && <CheckedIcon className='absolute right-4' />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ModelSelector
|
||||
popClassName="right-0"
|
||||
triggerIconSmall
|
||||
modelType={ModelType.textGeneration}
|
||||
supportAgentThought
|
||||
value={{
|
||||
modelName: modelId,
|
||||
providerName,
|
||||
}}
|
||||
onChange={(model) => {
|
||||
onChange?.(model.model_name, model.model_provider.provider_name)
|
||||
}}
|
||||
readonly={readonly}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -39,16 +39,20 @@ import type { DataSet } from '@/models/datasets'
|
||||
import ConfigSummary from '@/app/components/explore/universal-chat/config-view/summary'
|
||||
import { fetchDatasets } from '@/service/datasets'
|
||||
import ItemOperation from '@/app/components/explore/item-operation'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
|
||||
const APP_ID = 'universal-chat'
|
||||
const DEFAULT_MODEL_ID = 'gpt-3.5-turbo' // gpt-4, claude-2
|
||||
const DEFAULT_PLUGIN = {
|
||||
google_search: false,
|
||||
web_reader: true,
|
||||
wikipedia: true,
|
||||
}
|
||||
const CONFIG_KEY = 'universal-chat-config'
|
||||
// Old configuration structure is not compatible with the current configuration
|
||||
localStorage.removeItem('universal-chat-config')
|
||||
const CONFIG_KEY = 'universal-chat-config-2'
|
||||
type CONFIG = {
|
||||
providerName: string
|
||||
modelId: string
|
||||
plugin: {
|
||||
google_search: boolean
|
||||
@@ -61,13 +65,6 @@ const setPrevConfig = (config: CONFIG) => {
|
||||
prevConfig = config
|
||||
localStorage.setItem(CONFIG_KEY, JSON.stringify(prevConfig))
|
||||
}
|
||||
const getInitConfig = (type: 'model' | 'plugin') => {
|
||||
if (type === 'model')
|
||||
return prevConfig?.modelId || DEFAULT_MODEL_ID
|
||||
|
||||
if (type === 'plugin')
|
||||
return prevConfig?.plugin || DEFAULT_PLUGIN
|
||||
}
|
||||
|
||||
export type IMainProps = {}
|
||||
|
||||
@@ -75,6 +72,18 @@ const Main: FC<IMainProps> = () => {
|
||||
const { t } = useTranslation()
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
const { agentThoughtModelList } = useProviderContext()
|
||||
const getInitConfig = (type: 'model' | 'plugin') => {
|
||||
if (type === 'model') {
|
||||
return {
|
||||
providerName: prevConfig?.providerName || agentThoughtModelList?.[0]?.model_provider.provider_name,
|
||||
modelId: prevConfig?.modelId || agentThoughtModelList?.[0]?.model_name,
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'plugin')
|
||||
return prevConfig?.plugin || DEFAULT_PLUGIN
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.title = `${t('explore.sidebar.chat')} - Dify`
|
||||
@@ -441,8 +450,14 @@ const Main: FC<IMainProps> = () => {
|
||||
const [isResponsingConIsCurrCon, setIsResponsingConCurrCon, getIsResponsingConIsCurrCon] = useGetState(true)
|
||||
const handleSend = async (message: string) => {
|
||||
if (isNewConversation) {
|
||||
const isModelSelected = modelId && !!agentThoughtModelList.find(item => item.model_name === modelId)
|
||||
if (!isModelSelected) {
|
||||
notify({ type: 'error', message: t('appDebug.errorMessage.notSelectModel') })
|
||||
return
|
||||
}
|
||||
setPrevConfig({
|
||||
modelId,
|
||||
providerName,
|
||||
plugin: plugins as any,
|
||||
})
|
||||
}
|
||||
@@ -468,6 +483,7 @@ const Main: FC<IMainProps> = () => {
|
||||
query: message,
|
||||
conversation_id: isNewConversation ? null : currConversationId,
|
||||
model: modelId,
|
||||
provider: providerName,
|
||||
tools: [...formattedPlugins, ...formattedDataSets],
|
||||
}
|
||||
|
||||
@@ -629,8 +645,9 @@ const Main: FC<IMainProps> = () => {
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const [modelId, setModeId] = useState<string>(getInitConfig('model') as string)
|
||||
const initConfig = getInitConfig('model')
|
||||
const [modelId, setModeId] = useState<string>((initConfig as any)?.modelId as string)
|
||||
const [providerName, setProviderName] = useState<ProviderEnum>((initConfig as any)?.providerName as ProviderEnum)
|
||||
// const currModel = MODEL_LIST.find(item => item.id === modelId)
|
||||
|
||||
const [plugins, setPlugins] = useState<Record<string, boolean>>(getInitConfig('plugin') as Record<string, boolean>)
|
||||
@@ -642,7 +659,9 @@ const Main: FC<IMainProps> = () => {
|
||||
}
|
||||
const [dataSets, setDateSets] = useState<DataSet[]>([])
|
||||
const configSetDefaultValue = () => {
|
||||
setModeId(getInitConfig('model') as string)
|
||||
const initConfig = getInitConfig('model')
|
||||
setModeId((initConfig as any)?.modelId as string)
|
||||
setProviderName((initConfig as any)?.providerName as ProviderEnum)
|
||||
setPlugins(getInitConfig('plugin') as any)
|
||||
setDateSets([])
|
||||
}
|
||||
@@ -689,6 +708,7 @@ const Main: FC<IMainProps> = () => {
|
||||
<div className='flex items-center shrink-0 ml-2 space-x-2'>
|
||||
<ConfigSummary
|
||||
modelId={modelId}
|
||||
providerName={providerName}
|
||||
plugins={plugins}
|
||||
dataSets={dataSets}
|
||||
/>
|
||||
@@ -712,7 +732,11 @@ const Main: FC<IMainProps> = () => {
|
||||
isShowConfigElem={isNewConversation && chatList.length === 0}
|
||||
configElem={<Init
|
||||
modelId={modelId}
|
||||
onModelChange={setModeId}
|
||||
providerName={providerName}
|
||||
onModelChange={(modelId, providerName) => {
|
||||
setModeId(modelId)
|
||||
setProviderName(providerName)
|
||||
}}
|
||||
plugins={plugins}
|
||||
onPluginChange={handlePluginsChange}
|
||||
dataSets={dataSets}
|
||||
|
||||
Reference in New Issue
Block a user