Model Runtime (#1858)
Co-authored-by: StyleZhang <jasonapring2015@outlook.com> Co-authored-by: Garfield Dai <dai.hai@foxmail.com> Co-authored-by: chenhe <guchenhe@gmail.com> Co-authored-by: jyong <jyong@dify.ai> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: Yeuoly <admin@srmxy.cn>
265
web/app/components/app/annotation/mock-data.ts
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
import type { AnnotationItem, HitHistoryItem } from './type'
|
||||
|
||||
const list: AnnotationItem[] = [
|
||||
// create some mock data
|
||||
{
|
||||
id: '1',
|
||||
question: 'What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?',
|
||||
answer: 'What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?',
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
hit_count: 1,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
question: 'What is the capital of Canada?',
|
||||
answer: 'Ottawa',
|
||||
created_at: '2020-01-02T00:00:00Z',
|
||||
hit_count: 2,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
question: 'What is the capital of Mexico?',
|
||||
answer: 'Mexico City',
|
||||
created_at: '2020-01-03T00:00:00Z',
|
||||
hit_count: 3,
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
question: 'What is the capital of Brazil?',
|
||||
answer: 'Brasilia',
|
||||
created_at: '2020-01-04T00:00:00Z',
|
||||
hit_count: 4,
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
question: 'What is the capital of Argentina?',
|
||||
answer: 'Buenos Aires',
|
||||
created_at: '2020-01-05T00:00:00Z',
|
||||
hit_count: 5,
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
question: 'What is the capital of Chile?',
|
||||
answer: 'Santiago',
|
||||
created_at: '2020-01-06T00:00:00Z',
|
||||
hit_count: 6,
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
question: 'What is the capital of Peru?',
|
||||
answer: 'Lima',
|
||||
created_at: '2020-01-07T00:00:00Z',
|
||||
hit_count: 7,
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
question: 'What is the capital of Ecuador?',
|
||||
answer: 'Quito',
|
||||
created_at: '2020-01-08T00:00:00Z',
|
||||
hit_count: 8,
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
question: 'What is the capital of Colombia?',
|
||||
answer: 'Bogota',
|
||||
created_at: '2020-01-09T00:00:00Z',
|
||||
hit_count: 9,
|
||||
},
|
||||
]
|
||||
|
||||
export const hitHistoryList: HitHistoryItem[] = [
|
||||
// create some mock data. source can only be: API/Webapp/Explore/Debug
|
||||
{
|
||||
id: '1',
|
||||
question: 'What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?What is the capital of the United States?',
|
||||
source: 'API',
|
||||
score: 0.9,
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
question: 'What is the capital of Canada?',
|
||||
source: 'Webapp',
|
||||
score: 0.8,
|
||||
created_at: '2020-01-02T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
question: 'What is the capital of Mexico?',
|
||||
source: 'Explore',
|
||||
score: 0.7,
|
||||
created_at: '2020-01-03T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
question: 'What is the capital of Brazil?',
|
||||
source: 'Debug',
|
||||
score: 0.6,
|
||||
created_at: '2020-01-04T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
question: 'What is the capital of Argentina?',
|
||||
source: 'API',
|
||||
score: 0.5,
|
||||
created_at: '2020-01-05T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
question: 'What is the capital of Chile?',
|
||||
source: 'Webapp',
|
||||
score: 0.4,
|
||||
created_at: '2020-01-06T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '7',
|
||||
question: 'What is the capital of Peru?',
|
||||
source: 'Explore',
|
||||
score: 0.3,
|
||||
created_at: '2020-01-07T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '8',
|
||||
question: 'What is the capital of Ecuador?',
|
||||
source: 'Debug',
|
||||
score: 0.2,
|
||||
created_at: '2020-01-08T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '9',
|
||||
question: 'What is the capital of Colombia?',
|
||||
source: 'API',
|
||||
score: 0.1,
|
||||
created_at: '2020-01-09T00:00:00Z',
|
||||
},
|
||||
// make more mock data
|
||||
{
|
||||
id: '10',
|
||||
question: 'What is the capital of the United States?',
|
||||
source: 'API',
|
||||
score: 0.9,
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '11',
|
||||
question: 'What is the capital of Canada?',
|
||||
source: 'Webapp',
|
||||
score: 0.8,
|
||||
created_at: '2020-01-02T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '12',
|
||||
question: 'What is the capital of Mexico?',
|
||||
source: 'Explore',
|
||||
score: 0.7,
|
||||
created_at: '2020-01-03T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '13',
|
||||
question: 'What is the capital of Brazil?',
|
||||
source: 'Debug',
|
||||
score: 0.6,
|
||||
created_at: '2020-01-04T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '14',
|
||||
question: 'What is the capital of Argentina?',
|
||||
source: 'API',
|
||||
score: 0.5,
|
||||
created_at: '2020-01-05T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '15',
|
||||
question: 'What is the capital of Chile?',
|
||||
source: 'Webapp',
|
||||
score: 0.4,
|
||||
created_at: '2020-01-06T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '16',
|
||||
question: 'What is the capital of Peru?',
|
||||
source: 'Explore',
|
||||
score: 0.3,
|
||||
created_at: '2020-01-07T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '17',
|
||||
question: 'What is the capital of Ecuador?',
|
||||
source: 'Debug',
|
||||
score: 0.2,
|
||||
created_at: '2020-01-08T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '18',
|
||||
question: 'What is the capital of Colombia?',
|
||||
source: 'API',
|
||||
score: 0.1,
|
||||
created_at: '2020-01-09T00:00:00Z',
|
||||
},
|
||||
// make more mock data
|
||||
{
|
||||
id: '19',
|
||||
question: 'What is the capital of the United States?',
|
||||
source: 'API',
|
||||
score: 0.9,
|
||||
created_at: '2020-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '20',
|
||||
question: 'What is the capital of Canada?',
|
||||
source: 'Webapp',
|
||||
score: 0.8,
|
||||
created_at: '2020-01-02T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '21',
|
||||
question: 'What is the capital of Mexico?',
|
||||
source: 'Explore',
|
||||
score: 0.7,
|
||||
created_at: '2020-01-03T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '22',
|
||||
question: 'What is the capital of Brazil?',
|
||||
source: 'Debug',
|
||||
score: 0.6,
|
||||
created_at: '2020-01-04T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '23',
|
||||
question: 'What is the capital of Argentina?',
|
||||
source: 'API',
|
||||
score: 0.5,
|
||||
created_at: '2020-01-05T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '24',
|
||||
question: 'What is the capital of Chile?',
|
||||
source: 'Webapp',
|
||||
score: 0.4,
|
||||
created_at: '2020-01-06T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '25',
|
||||
question: 'What is the capital of Peru?',
|
||||
source: 'Explore',
|
||||
score: 0.3,
|
||||
created_at: '2020-01-07T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '26',
|
||||
question: 'What is the capital of Ecuador?',
|
||||
source: 'Debug',
|
||||
score: 0.2,
|
||||
created_at: '2020-01-08T00:00:00Z',
|
||||
},
|
||||
{
|
||||
id: '27',
|
||||
question: 'What is the capital of Colombia?',
|
||||
source: 'API',
|
||||
score: 0.1,
|
||||
created_at: '2020-01-09T00:00:00Z',
|
||||
},
|
||||
]
|
||||
|
||||
export default list
|
||||
|
|
@ -72,7 +72,16 @@ export type IChatItem = {
|
|||
|
||||
export type MessageEnd = {
|
||||
id: string
|
||||
retriever_resources?: CitationItem[]
|
||||
metadata: {
|
||||
retriever_resources?: CitationItem[]
|
||||
annotation_reply: {
|
||||
id: string
|
||||
account: {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type MessageReplace = {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,6 @@ import { useBoolean, useClickAway, useGetState } from 'ahooks'
|
|||
import { InformationCircleIcon } from '@heroicons/react/24/outline'
|
||||
import produce from 'immer'
|
||||
import ParamItem from './param-item'
|
||||
import ModelIcon from './model-icon'
|
||||
import ModelName from './model-name'
|
||||
import ModelModeTypeLabel from './model-mode-type-label'
|
||||
import { SlidersH } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||
import Radio from '@/app/components/base/radio'
|
||||
import Panel from '@/app/components/base/panel'
|
||||
|
|
@ -24,17 +21,19 @@ import { Target04 } from '@/app/components/base/icons/src/vender/solid/general'
|
|||
import { Sliders02 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
|
||||
import { fetchModelParams } from '@/service/debug'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import { ModelType, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
export type IConfigModelProps = {
|
||||
isAdvancedMode: boolean
|
||||
mode: string
|
||||
modelId: string
|
||||
provider: ProviderEnum
|
||||
setModel: (model: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => void
|
||||
provider: string
|
||||
setModel: (model: { id: string; provider: string; mode: ModelModeType; features: string[] }) => void
|
||||
completionParams: CompletionParams
|
||||
onCompletionParamsChange: (newParams: CompletionParams) => void
|
||||
disabled: boolean
|
||||
|
|
@ -50,11 +49,16 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
disabled,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const [isShowConfig, { setFalse: hideConfig, toggle: toogleShowConfig }] = useBoolean(false)
|
||||
const [maxTokenSettingTipVisible, setMaxTokenSettingTipVisible] = useState(false)
|
||||
const configContentRef = React.useRef(null)
|
||||
const currModel = textGenerationModelList.find(item => item.model_name === modelId)
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel: currModel,
|
||||
textGenerationModelList,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{ provider, model: modelId },
|
||||
)
|
||||
|
||||
const media = useBreakpoints()
|
||||
const isMobile = media === MediaType.mobile
|
||||
|
|
@ -81,7 +85,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
setAllParams(newAllParams)
|
||||
}
|
||||
})()
|
||||
}, [provider, modelId])
|
||||
}, [provider, modelId, allParams, setAllParams])
|
||||
|
||||
useClickAway(() => {
|
||||
hideConfig()
|
||||
|
|
@ -89,7 +93,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
|
||||
const selectedModel = { name: modelId } // options.find(option => option.id === modelId)
|
||||
|
||||
const ensureModelParamLoaded = (provider: ProviderEnum, modelId: string) => {
|
||||
const ensureModelParamLoaded = (provider: string, modelId: string) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (getAllParams()[provider]?.[modelId]) {
|
||||
resolve()
|
||||
|
|
@ -126,13 +130,13 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
return adjustedValue
|
||||
}
|
||||
|
||||
const handleSelectModel = ({ id, provider: nextProvider, mode, features }: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => {
|
||||
const handleSelectModel = ({ id, provider: nextProvider, mode, features }: { id: string; provider: string; mode: ModelModeType; features: string[] }) => {
|
||||
return async () => {
|
||||
const prevParamsRule = getAllParams()[provider]?.[modelId]
|
||||
|
||||
setModel({
|
||||
id,
|
||||
provider: nextProvider || ProviderEnum.openai,
|
||||
provider: nextProvider || 'openai',
|
||||
mode,
|
||||
features,
|
||||
})
|
||||
|
|
@ -269,26 +273,34 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
|
||||
const max = currParams.max_tokens.max
|
||||
const isSupportMaxToken = currParams.max_tokens.enabled
|
||||
if (isSupportMaxToken && currModel?.model_provider.provider_name !== ProviderEnum.anthropic && completionParams.max_tokens > max * 2 / 3)
|
||||
if (isSupportMaxToken && currentProvider?.provider !== 'anthropic' && completionParams.max_tokens > max * 2 / 3)
|
||||
setMaxTokenSettingTipVisible(true)
|
||||
else
|
||||
setMaxTokenSettingTipVisible(false)
|
||||
}, [currParams, completionParams.max_tokens, setMaxTokenSettingTipVisible])
|
||||
}, [currParams, completionParams.max_tokens, setMaxTokenSettingTipVisible, currentProvider])
|
||||
return (
|
||||
<div className='relative' ref={configContentRef}>
|
||||
<div
|
||||
className={cn('flex items-center border h-8 px-2 space-x-2 rounded-lg', disabled ? diabledStyle : ableStyle)}
|
||||
onClick={() => !disabled && toogleShowConfig()}
|
||||
>
|
||||
<ModelIcon
|
||||
className='!w-5 !h-5'
|
||||
modelId={modelId}
|
||||
providerName={provider}
|
||||
/>
|
||||
<div className='text-[13px] text-gray-900 font-medium'>
|
||||
<ModelName modelId={selectedModel.name} modelDisplayName={currModel?.model_display_name} />
|
||||
</div>
|
||||
{isAdvancedMode && <ModelModeTypeLabel type={currModel?.model_mode as ModelModeType} isHighlight />}
|
||||
{
|
||||
currentProvider && (
|
||||
<ModelIcon
|
||||
className='!w-5 !h-5'
|
||||
provider={currentProvider}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
currModel && (
|
||||
<ModelName
|
||||
className='text-gray-900'
|
||||
modelItem={currModel}
|
||||
showMode={isAdvancedMode}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{disabled ? <InformationCircleIcon className='w-4 h-4 text-[#F79009]' /> : <SlidersH className='w-4 h-4 text-indigo-600' />}
|
||||
</div>
|
||||
{isShowConfig && (
|
||||
|
|
@ -312,21 +324,16 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
<div className="flex items-center justify-between my-5 h-9">
|
||||
<div>{t('appDebug.modelConfig.model')}</div>
|
||||
<ModelSelector
|
||||
isShowModelModeType={isAdvancedMode}
|
||||
isShowAddModel
|
||||
popClassName='right-0'
|
||||
triggerIconSmall
|
||||
value={{
|
||||
modelName: modelId,
|
||||
providerName: provider,
|
||||
}}
|
||||
modelType={ModelType.textGeneration}
|
||||
onChange={(model) => {
|
||||
defaultModel={{ model: modelId, provider }}
|
||||
modelList={textGenerationModelList}
|
||||
onSelect={({ provider, model }) => {
|
||||
const targetProvider = textGenerationModelList.find(modelItem => modelItem.provider === provider)
|
||||
const targetModelItem = targetProvider?.models.find(modelItem => modelItem.model === model)
|
||||
handleSelectModel({
|
||||
id: model.model_name,
|
||||
provider: model.model_provider.provider_name as ProviderEnum,
|
||||
mode: model.model_mode,
|
||||
features: model.features,
|
||||
id: model,
|
||||
provider,
|
||||
mode: targetModelItem?.model_properties.mode as ModelModeType,
|
||||
features: targetModelItem?.features || [],
|
||||
})()
|
||||
}}
|
||||
/>
|
||||
|
|
@ -336,7 +343,7 @@ const ConfigModel: FC<IConfigModelProps> = ({
|
|||
)}
|
||||
|
||||
{/* Tone type */}
|
||||
{[ProviderEnum.openai, ProviderEnum.azure_openai].includes(provider) && (
|
||||
{['openai', 'azure_openai'].includes(provider) && (
|
||||
<div className="mt-5 mb-4">
|
||||
<div className="mb-3 text-sm text-gray-900">{t('appDebug.modelConfig.setTone')}</div>
|
||||
<Radio.Group className={cn('!rounded-lg', toneTabBgClassName)} value={toneId} onChange={handleToneChange}>
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
OpenaiGreen,
|
||||
OpenaiViolet,
|
||||
} from '@/app/components/base/icons/src/public/llm'
|
||||
import { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import ProviderConfig from '@/app/components/header/account-setting/model-page/configs'
|
||||
|
||||
export type IModelIconProps = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
className?: string
|
||||
}
|
||||
|
||||
const ModelIcon: FC<IModelIconProps> = ({ modelId, providerName, className }) => {
|
||||
let Icon = <OpenaiGreen className='w-full h-full' />
|
||||
if (providerName === ProviderEnum.openai)
|
||||
Icon = modelId.includes('gpt-4') ? <OpenaiViolet className='w-full h-full' /> : <OpenaiGreen className='w-full h-full' />
|
||||
else
|
||||
Icon = ProviderConfig[providerName]?.selector.icon
|
||||
|
||||
return (
|
||||
<div className={cn(className, 'w-4 h-4')}>
|
||||
{Icon}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(ModelIcon)
|
||||
|
|
@ -21,10 +21,10 @@ import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
|
|||
import ConfigVar from '@/app/components/app/configuration/config-var'
|
||||
import type { CitationConfig, ModelConfig, ModerationConfig, MoreLikeThisConfig, PromptVariable, SpeechToTextConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug'
|
||||
import { AppType, ModelModeType } from '@/types/app'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import ConfigParamModal from '@/app/components/app/configuration/toolbox/annotation/config-param-modal'
|
||||
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
const Config: FC = () => {
|
||||
const {
|
||||
|
|
@ -55,7 +55,7 @@ const Config: FC = () => {
|
|||
setModerationConfig,
|
||||
} = useContext(ConfigContext)
|
||||
const isChatApp = mode === AppType.chat
|
||||
const { speech2textDefaultModel } = useProviderContext()
|
||||
const { data: speech2textDefaultModel } = useDefaultModel(4)
|
||||
const { setShowModerationSettingModal } = useModalContext()
|
||||
|
||||
const promptTemplate = modelConfig.configs.prompt_template
|
||||
|
|
|
|||
|
|
@ -12,15 +12,14 @@ import Modal from '@/app/components/base/modal'
|
|||
import Button from '@/app/components/base/button'
|
||||
import RadioCard from '@/app/components/base/radio-card/simple'
|
||||
import { RETRIEVE_TYPE } from '@/types/app'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { DATASET_DEFAULT } from '@/config'
|
||||
import {
|
||||
MultiPathRetrieval,
|
||||
NTo1Retrieval,
|
||||
} from '@/app/components/base/icons/src/public/common'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
const ParamsConfig: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
|
|
@ -38,11 +37,11 @@ const ParamsConfig: FC = () => {
|
|||
retrieval_model: value,
|
||||
})
|
||||
}
|
||||
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
} = useProviderContext()
|
||||
modelList: rerankModelList,
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(3)
|
||||
|
||||
const rerankModel = (() => {
|
||||
if (tempDataSetConfigs.reranking_model) {
|
||||
|
|
@ -53,8 +52,8 @@ const ParamsConfig: FC = () => {
|
|||
}
|
||||
else if (rerankDefaultModel) {
|
||||
return {
|
||||
provider_name: rerankDefaultModel.model_provider.provider_name,
|
||||
model_name: rerankDefaultModel.model_name,
|
||||
provider_name: rerankDefaultModel.provider.provider,
|
||||
model_name: rerankDefaultModel.model,
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
@ -104,8 +103,8 @@ const ParamsConfig: FC = () => {
|
|||
const config = { ...tempDataSetConfigs }
|
||||
if (config.retrieval_model === RETRIEVE_TYPE.multiWay && !config.reranking_model) {
|
||||
config.reranking_model = {
|
||||
reranking_provider_name: rerankDefaultModel?.model_provider.provider_name,
|
||||
reranking_model_name: rerankDefaultModel?.model_name,
|
||||
reranking_provider_name: rerankDefaultModel?.provider,
|
||||
reranking_model_name: rerankDefaultModel?.model,
|
||||
} as any
|
||||
}
|
||||
setDatasetConfigs(config)
|
||||
|
|
@ -163,18 +162,17 @@ const ParamsConfig: FC = () => {
|
|||
<div className='leading-[32px] text-[13px] font-medium text-gray-900'>{t('common.modelProvider.rerankModel.key')}</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
popClassName='!max-w-[100%] !w-full'
|
||||
value={rerankModel && { providerName: rerankModel.provider_name, modelName: rerankModel.model_name } as any}
|
||||
modelType={ModelType.reranking}
|
||||
onChange={(v) => {
|
||||
defaultModel={rerankModel && { provider: rerankModel?.provider_name, model: rerankModel?.model_name }}
|
||||
onSelect={(v) => {
|
||||
setTempDataSetConfigs({
|
||||
...tempDataSetConfigs,
|
||||
reranking_model: {
|
||||
reranking_provider_name: v.model_provider.provider_name,
|
||||
reranking_model_name: v.model_name,
|
||||
reranking_provider_name: v.provider,
|
||||
reranking_model_name: v.model,
|
||||
},
|
||||
})
|
||||
}}
|
||||
modelList={rerankModelList}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,9 +6,6 @@ import cn from 'classnames'
|
|||
import { BookOpenIcon } from '@heroicons/react/24/outline'
|
||||
import IndexMethodRadio from '@/app/components/datasets/settings/index-method-radio'
|
||||
import Button from '@/app/components/base/button'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import { updateDatasetSetting } from '@/service/datasets'
|
||||
|
|
@ -17,10 +14,14 @@ import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
|||
import type { RetrievalConfig } from '@/types/app'
|
||||
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
|
||||
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import PermissionsRadio from '@/app/components/datasets/settings/permissions-radio'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import {
|
||||
useModelList,
|
||||
useModelListAndDefaultModelAndCurrentProviderAndModel,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type SettingsModalProps = {
|
||||
currentDataset: DataSet
|
||||
|
|
@ -41,6 +42,12 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||
onCancel,
|
||||
onSave,
|
||||
}) => {
|
||||
const { data: embeddingsModelList } = useModelList(2)
|
||||
const {
|
||||
modelList: rerankModelList,
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(3)
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useToastContext()
|
||||
const ref = useRef(null)
|
||||
|
|
@ -51,12 +58,6 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||
const [indexMethod, setIndexMethod] = useState(currentDataset.indexing_technique)
|
||||
const [retrievalConfig, setRetrievalConfig] = useState(localeCurrentDataset?.retrieval_model_dict as RetrievalConfig)
|
||||
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
} = useProviderContext()
|
||||
|
||||
const handleValueChange = (type: string, value: string) => {
|
||||
setLocaleCurrentDataset({ ...localeCurrentDataset, [type]: value })
|
||||
}
|
||||
|
|
@ -73,7 +74,7 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||
if (
|
||||
!isReRankModelSelected({
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
isRerankDefaultModelVaild: !!isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
retrievalConfig,
|
||||
indexMethod,
|
||||
|
|
@ -204,12 +205,11 @@ const SettingsModal: FC<SettingsModalProps> = ({
|
|||
<div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
|
||||
<ModelSelector
|
||||
readonly
|
||||
value={{
|
||||
providerName: localeCurrentDataset.embedding_model_provider as ProviderEnum,
|
||||
modelName: localeCurrentDataset.embedding_model,
|
||||
defaultModel={{
|
||||
provider: localeCurrentDataset.embedding_model_provider,
|
||||
model: localeCurrentDataset.embedding_model,
|
||||
}}
|
||||
modelType={ModelType.embeddings}
|
||||
onChange={() => {}}
|
||||
modelList={embeddingsModelList}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-2 w-full text-xs leading-6 text-gray-500'>
|
||||
|
|
|
|||
|
|
@ -24,10 +24,11 @@ import type { ModelConfig as BackendModelConfig, VisionFile } from '@/types/app'
|
|||
import { promptVariablesToUserInputsForm } from '@/utils/model-config'
|
||||
import TextGeneration from '@/app/components/app/text-generate/item'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import type { Inputs } from '@/models/debug'
|
||||
import { fetchFileUploadConfig } from '@/service/common'
|
||||
import type { Annotation as AnnotationType } from '@/models/log'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type IDebug = {
|
||||
hasSetAPIKEY: boolean
|
||||
onSetting: () => void
|
||||
|
|
@ -69,7 +70,7 @@ const Debug: FC<IDebug> = ({
|
|||
visionConfig,
|
||||
annotationConfig,
|
||||
} = useContext(ConfigContext)
|
||||
const { speech2textDefaultModel } = useProviderContext()
|
||||
const { data: speech2textDefaultModel } = useDefaultModel(4)
|
||||
const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
|
||||
const chatListDomRef = useRef<HTMLDivElement>(null)
|
||||
const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig)
|
||||
|
|
@ -346,7 +347,27 @@ const Debug: FC<IDebug> = ({
|
|||
}
|
||||
},
|
||||
onMessageEnd: (messageEnd) => {
|
||||
responseItem.citation = messageEnd.retriever_resources
|
||||
// TODO
|
||||
if (messageEnd.metadata?.annotation_reply) {
|
||||
responseItem.id = messageEnd.id
|
||||
responseItem.annotation = ({
|
||||
id: messageEnd.metadata.annotation_reply.id,
|
||||
authorName: messageEnd.metadata.annotation_reply.account.name,
|
||||
} as AnnotationType)
|
||||
const newListWithAnswer = produce(
|
||||
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
|
||||
(draft) => {
|
||||
if (!draft.find(item => item.id === questionId))
|
||||
draft.push({ ...questionItem })
|
||||
|
||||
draft.push({
|
||||
...responseItem,
|
||||
})
|
||||
})
|
||||
setChatList(newListWithAnswer)
|
||||
return
|
||||
}
|
||||
responseItem.citation = messageEnd.metadata?.retriever_resources || []
|
||||
|
||||
const newListWithAnswer = produce(
|
||||
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
|
||||
|
|
@ -362,6 +383,7 @@ const Debug: FC<IDebug> = ({
|
|||
responseItem.content = messageReplace.answer
|
||||
},
|
||||
onAnnotationReply: (annotationReply) => {
|
||||
// TODO: temp debug
|
||||
responseItem.id = annotationReply.id
|
||||
responseItem.content = annotationReply.answer
|
||||
responseItem.annotation = ({
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import { useState } from 'react'
|
||||
import { clone } from 'lodash-es'
|
||||
import produce from 'immer'
|
||||
import type { ChatPromptConfig, CompletionParams, CompletionPromptConfig, ConversationHistoriesRole, PromptItem } from '@/models/debug'
|
||||
import type { ChatPromptConfig, CompletionPromptConfig, ConversationHistoriesRole, PromptItem } from '@/models/debug'
|
||||
import { PromptMode } from '@/models/debug'
|
||||
import { AppType, ModelModeType } from '@/types/app'
|
||||
import { DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
|
||||
import { PRE_PROMPT_PLACEHOLDER_TEXT, checkHasContextBlock, checkHasHistoryBlock, checkHasQueryBlock } from '@/app/components/base/prompt-editor/constants'
|
||||
import { fetchPromptTemplate } from '@/service/debug'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
||||
type Param = {
|
||||
appMode: string
|
||||
|
|
@ -16,8 +17,8 @@ type Param = {
|
|||
prePrompt: string
|
||||
onUserChangedPrompt: () => void
|
||||
hasSetDataSet: boolean
|
||||
completionParams: CompletionParams
|
||||
setCompletionParams: (params: CompletionParams) => void
|
||||
completionParams: FormValue
|
||||
setCompletionParams: (params: FormValue) => void
|
||||
setStop: (stop: string[]) => void
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ import useAdvancedPromptConfig from './hooks/use-advanced-prompt-config'
|
|||
import EditHistoryModal from './config-prompt/conversation-histroy/edit-modal'
|
||||
import type {
|
||||
AnnotationReplyConfig,
|
||||
CompletionParams,
|
||||
DatasetConfigs,
|
||||
Inputs,
|
||||
ModelConfig,
|
||||
|
|
@ -29,11 +28,11 @@ import type { ExternalDataTool } from '@/models/common'
|
|||
import type { DataSet } from '@/models/datasets'
|
||||
import type { ModelConfig as BackendModelConfig, VisionSettings } from '@/types/app'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import ConfigModel from '@/app/components/app/configuration/config-model'
|
||||
// import ConfigModel from '@/app/components/app/configuration/config-model'
|
||||
import Config from '@/app/components/app/configuration/config'
|
||||
import Debug from '@/app/components/app/configuration/debug'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import { ModelFeature, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { fetchAppDetail, updateAppModelConfig } from '@/service/apps'
|
||||
import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables } from '@/utils/model-config'
|
||||
|
|
@ -48,10 +47,13 @@ import I18n from '@/context/i18n'
|
|||
import { useModalContext } from '@/context/modal-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
|
||||
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type PublichConfig = {
|
||||
modelConfig: ModelConfig
|
||||
completionParams: CompletionParams
|
||||
completionParams: FormValue
|
||||
}
|
||||
|
||||
const Configuration: FC = () => {
|
||||
|
|
@ -112,16 +114,9 @@ const Configuration: FC = () => {
|
|||
const [externalDataToolsConfig, setExternalDataToolsConfig] = useState<ExternalDataTool[]>([])
|
||||
const [inputs, setInputs] = useState<Inputs>({})
|
||||
const [query, setQuery] = useState('')
|
||||
const [completionParams, doSetCompletionParams] = useState<CompletionParams>({
|
||||
max_tokens: 16,
|
||||
temperature: 1, // 0-2
|
||||
top_p: 1,
|
||||
presence_penalty: 1, // -2-2
|
||||
frequency_penalty: 1, // -2-2
|
||||
stop: [],
|
||||
})
|
||||
const [completionParams, doSetCompletionParams] = useState<FormValue>({})
|
||||
const [tempStop, setTempStop, getTempStop] = useGetState<string[]>([])
|
||||
const setCompletionParams = (value: CompletionParams) => {
|
||||
const setCompletionParams = (value: FormValue) => {
|
||||
const params = { ...value }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
|
|
@ -133,7 +128,7 @@ const Configuration: FC = () => {
|
|||
}
|
||||
|
||||
const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
|
||||
provider: ProviderEnum.openai,
|
||||
provider: 'openai',
|
||||
model_id: 'gpt-3.5-turbo',
|
||||
mode: ModelModeType.unset,
|
||||
configs: {
|
||||
|
|
@ -223,28 +218,21 @@ const Configuration: FC = () => {
|
|||
})
|
||||
}
|
||||
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const currModel = textGenerationModelList.find(item => item.model_name === modelConfig.model_id)
|
||||
const hasSetCustomAPIKEY = !!textGenerationModelList?.find(({ model_provider: provider }) => {
|
||||
if (provider.provider_type === 'system' && provider.quota_type === 'paid')
|
||||
return true
|
||||
|
||||
if (provider.provider_type === 'custom')
|
||||
return true
|
||||
|
||||
return false
|
||||
})
|
||||
const isTrailFinished = !hasSetCustomAPIKEY && textGenerationModelList
|
||||
.filter(({ model_provider: provider }) => provider.quota_type === 'trial')
|
||||
.every(({ model_provider: provider }) => {
|
||||
const { quota_used, quota_limit } = provider
|
||||
return quota_used === quota_limit
|
||||
})
|
||||
const { hasSettedApiKey } = useProviderContext()
|
||||
const {
|
||||
currentModel: currModel,
|
||||
textGenerationModelList,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{
|
||||
provider: modelConfig.provider,
|
||||
model: modelConfig.model_id,
|
||||
},
|
||||
)
|
||||
|
||||
// Fill old app data missing model mode.
|
||||
useEffect(() => {
|
||||
if (hasFetchedDetail && !modelModeType) {
|
||||
const mode = textGenerationModelList.find(({ model_name }) => model_name === modelConfig.model_id)?.model_mode
|
||||
const mode = currModel?.model_properties.mode as (ModelModeType | undefined)
|
||||
if (mode) {
|
||||
const newModelConfig = produce(modelConfig, (draft: ModelConfig) => {
|
||||
draft.mode = mode
|
||||
|
|
@ -252,9 +240,7 @@ const Configuration: FC = () => {
|
|||
setModelConfig(newModelConfig)
|
||||
}
|
||||
}
|
||||
}, [textGenerationModelList, hasFetchedDetail])
|
||||
|
||||
const hasSetAPIKEY = hasSetCustomAPIKEY || !isTrailFinished
|
||||
}, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel, modelConfig])
|
||||
|
||||
const [promptMode, doSetPromptMode] = useState(PromptMode.simple)
|
||||
const isAdvancedMode = promptMode === PromptMode.advanced
|
||||
|
|
@ -295,11 +281,11 @@ const Configuration: FC = () => {
|
|||
})
|
||||
|
||||
const setModel = async ({
|
||||
id: modelId,
|
||||
modelId,
|
||||
provider,
|
||||
mode: modeMode,
|
||||
features,
|
||||
}: { id: string; provider: ProviderEnum; mode: ModelModeType; features: string[] }) => {
|
||||
}: { modelId: string; provider: string; mode: string; features: string[] }) => {
|
||||
if (isAdvancedMode) {
|
||||
const appMode = mode
|
||||
|
||||
|
|
@ -321,11 +307,11 @@ const Configuration: FC = () => {
|
|||
const newModelConfig = produce(modelConfig, (draft: ModelConfig) => {
|
||||
draft.provider = provider
|
||||
draft.model_id = modelId
|
||||
draft.mode = modeMode
|
||||
draft.mode = modeMode as ModelModeType
|
||||
})
|
||||
|
||||
setModelConfig(newModelConfig)
|
||||
const supportVision = features && features.includes(ModelFeature.vision)
|
||||
const supportVision = features && features.includes(ModelFeatureEnum.vision)
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
setVisionConfig({
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
|
|
@ -334,7 +320,7 @@ const Configuration: FC = () => {
|
|||
}, true)
|
||||
}
|
||||
|
||||
const isShowVisionConfig = !!currModel?.features.includes(ModelFeature.vision)
|
||||
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
||||
const [visionConfig, doSetVisionConfig] = useState({
|
||||
enabled: false,
|
||||
number_limits: 2,
|
||||
|
|
@ -566,8 +552,8 @@ const Configuration: FC = () => {
|
|||
return (
|
||||
<ConfigContext.Provider value={{
|
||||
appId,
|
||||
hasSetAPIKEY,
|
||||
isTrailFinished,
|
||||
hasSetAPIKEY: hasSettedApiKey,
|
||||
isTrailFinished: false,
|
||||
mode,
|
||||
modelModeType,
|
||||
promptMode,
|
||||
|
|
@ -660,17 +646,17 @@ const Configuration: FC = () => {
|
|||
|
||||
<div className='flex items-center flex-wrap gap-y-2 gap-x-2'>
|
||||
{/* Model and Parameters */}
|
||||
<ConfigModel
|
||||
<ModelParameterModal
|
||||
isAdvancedMode={isAdvancedMode}
|
||||
mode={mode}
|
||||
provider={modelConfig.provider as ProviderEnum}
|
||||
provider={modelConfig.provider}
|
||||
completionParams={completionParams}
|
||||
modelId={modelConfig.model_id}
|
||||
setModel={setModel}
|
||||
onCompletionParamsChange={(newParams: CompletionParams) => {
|
||||
setModel={setModel as any}
|
||||
onCompletionParamsChange={(newParams: FormValue) => {
|
||||
setCompletionParams(newParams)
|
||||
}}
|
||||
disabled={!hasSetAPIKEY}
|
||||
disabled={!hasSettedApiKey}
|
||||
/>
|
||||
<div className='w-[1px] h-[14px] bg-gray-200'></div>
|
||||
<Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>
|
||||
|
|
@ -689,7 +675,7 @@ const Configuration: FC = () => {
|
|||
</div>
|
||||
{!isMobile && <div className="relative w-1/2 grow h-full overflow-y-auto py-4 px-6 bg-gray-50 flex flex-col rounded-tl-2xl border-t border-l" style={{ borderColor: 'rgba(0, 0, 0, 0.02)' }}>
|
||||
<Debug
|
||||
hasSetAPIKEY={hasSetAPIKEY}
|
||||
hasSetAPIKEY={hasSettedApiKey}
|
||||
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
inputs={inputs}
|
||||
/>
|
||||
|
|
@ -744,7 +730,7 @@ const Configuration: FC = () => {
|
|||
{isMobile && (
|
||||
<Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
|
||||
<Debug
|
||||
hasSetAPIKEY={hasSetAPIKEY}
|
||||
hasSetAPIKEY={hasSettedApiKey}
|
||||
onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
inputs={inputs}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ import ScoreSlider from '../score-slider'
|
|||
import { Item } from './config-param'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector/portal-select'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import type { AnnotationReplyConfig } from '@/models/debug'
|
||||
import { ANNOTATION_DEFAULT } from '@/config'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type Props = {
|
||||
appId: string
|
||||
|
|
@ -34,9 +33,10 @@ const ConfigParamModal: FC<Props> = ({
|
|||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
embeddingsDefaultModel,
|
||||
isEmbeddingsDefaultModelValid,
|
||||
} = useProviderContext()
|
||||
modelList: embeddingsModelList,
|
||||
defaultModel: embeddingsDefaultModel,
|
||||
currentModel: isEmbeddingsDefaultModelValid,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(2)
|
||||
const [annotationConfig, setAnnotationConfig] = useState(oldAnnotationConfig)
|
||||
|
||||
const [isLoading, setLoading] = useState(false)
|
||||
|
|
@ -47,8 +47,8 @@ const ConfigParamModal: FC<Props> = ({
|
|||
}
|
||||
: (embeddingsDefaultModel
|
||||
? {
|
||||
providerName: embeddingsDefaultModel.model_provider.provider_name,
|
||||
modelName: embeddingsDefaultModel.model_name,
|
||||
providerName: embeddingsDefaultModel.provider.provider,
|
||||
modelName: embeddingsDefaultModel.model,
|
||||
}
|
||||
: undefined))
|
||||
const onHide = () => {
|
||||
|
|
@ -57,7 +57,7 @@ const ConfigParamModal: FC<Props> = ({
|
|||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model_name && !isEmbeddingsDefaultModelValid)) {
|
||||
if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model && !isEmbeddingsDefaultModelValid)) {
|
||||
Toast.notify({
|
||||
message: t('common.modelProvider.embeddingModel.required'),
|
||||
type: 'error',
|
||||
|
|
@ -106,13 +106,15 @@ const ConfigParamModal: FC<Props> = ({
|
|||
>
|
||||
<div className='pt-1'>
|
||||
<ModelSelector
|
||||
widthSameToTrigger
|
||||
value={embeddingModel as any}
|
||||
modelType={ModelType.embeddings}
|
||||
onChange={(val) => {
|
||||
defaultModel={embeddingModel && {
|
||||
provider: embeddingModel.providerName,
|
||||
model: embeddingModel.modelName,
|
||||
}}
|
||||
modelList={embeddingsModelList}
|
||||
onSelect={(val) => {
|
||||
setEmbeddingModel({
|
||||
providerName: val.model_provider.provider_name,
|
||||
modelName: val.model_name,
|
||||
providerName: val.provider,
|
||||
modelName: val.model,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import type { CodeBasedExtensionItem } from '@/models/common'
|
|||
import I18n from '@/context/i18n'
|
||||
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
||||
const systemTypes = ['openai_moderation', 'keywords', 'api']
|
||||
|
||||
|
|
@ -57,10 +58,11 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
|
|||
'/code-based-extension?module=moderation',
|
||||
fetchCodeBasedExtensionList,
|
||||
)
|
||||
const systemOpenaiProvider = modelProviders?.openai.providers.find(item => item.provider_type === 'system')
|
||||
const systemOpenaiProviderCanUse = systemOpenaiProvider && (((systemOpenaiProvider as any).quota_limit - (systemOpenaiProvider as any).quota_used) > 0)
|
||||
const customOpenaiProviders = modelProviders?.openai.providers.filter(item => item.provider_type !== 'system')
|
||||
const customOpenaiProvidersCanUse = customOpenaiProviders?.some(item => item.is_valid)
|
||||
const openaiProvider = modelProviders?.data.find(item => item.provider === 'openai')
|
||||
const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled
|
||||
const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined
|
||||
const systemOpenaiProviderCanUse = systemOpenaiProviderQuota?.is_valid
|
||||
const customOpenaiProvidersCanUse = openaiProvider?.custom_configuration.status === CustomConfigurationStatusEnum.active
|
||||
const openaiProviderConfiged = customOpenaiProvidersCanUse || systemOpenaiProviderCanUse
|
||||
const providers: Provider[] = [
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,9 +29,9 @@ import Tooltip from '@/app/components/base/tooltip'
|
|||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { fetchChatConversationDetail, fetchChatMessages, fetchCompletionConversationDetail, updateLogMessageAnnotations, updateLogMessageFeedbacks } from '@/service/log'
|
||||
import { TONE_LIST } from '@/config'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
||||
import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
|
||||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import TextGeneration from '@/app/components/app/text-generate/item'
|
||||
|
||||
|
|
@ -188,6 +188,12 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
|
|||
|
||||
const modelName = (detail.model_config as any).model.name
|
||||
const provideName = (detail.model_config as any).model.provider as any
|
||||
const {
|
||||
currentModel,
|
||||
currentProvider,
|
||||
} = useTextGenerationCurrentProviderAndModelAndModelList(
|
||||
{ provider: provideName, model: modelName },
|
||||
)
|
||||
const varList = (detail.model_config as any).user_input_form.map((item: any) => {
|
||||
const itemContent = item[Object.keys(item)[0]]
|
||||
return {
|
||||
|
|
@ -224,13 +230,13 @@ function DetailPanel<T extends ChatConversationFullDetailResponse | CompletionCo
|
|||
>
|
||||
<ModelIcon
|
||||
className='!w-5 !h-5'
|
||||
modelId={modelName}
|
||||
providerName={provideName}
|
||||
provider={currentProvider}
|
||||
modelName={currentModel?.model}
|
||||
/>
|
||||
<ModelName
|
||||
modelItem={currentModel!}
|
||||
showMode
|
||||
/>
|
||||
<div className='text-[13px] text-gray-900 font-medium'>
|
||||
<ModelName modelId={modelName} modelDisplayName={modelName} />
|
||||
</div>
|
||||
<ModelModeTypeLabel type={detail?.model_config.model.mode as any} isHighlight />
|
||||
</div>
|
||||
<Popover
|
||||
position='br'
|
||||
|
|
|
|||
|
|
@ -3,78 +3,35 @@ import type { FC } from 'react'
|
|||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import Progress from './progress'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { LinkExternal02, XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import I18n from '@/context/i18n'
|
||||
import ProviderConfig from '@/app/components/header/account-setting/model-page/configs'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
|
||||
const APIKeyInfoPanel: FC = () => {
|
||||
const isCloud = !IS_CE_EDITION
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const { hasSettedApiKey } = useProviderContext()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
|
||||
const { t } = useTranslation()
|
||||
|
||||
const [isShow, setIsShow] = useState(true)
|
||||
|
||||
const hasSetAPIKEY = !!textGenerationModelList?.find(({ model_provider: provider }) => {
|
||||
if (provider.provider_type === 'system' && provider.quota_type === 'paid')
|
||||
return true
|
||||
|
||||
if (provider.provider_type === 'custom')
|
||||
return true
|
||||
|
||||
return false
|
||||
})
|
||||
if (hasSetAPIKEY)
|
||||
if (hasSettedApiKey)
|
||||
return null
|
||||
|
||||
// first show in trail and not used exhausted, else find the exhausted
|
||||
const [used, total, unit, providerName] = (() => {
|
||||
if (!textGenerationModelList || !isCloud)
|
||||
return [0, 0, '', '']
|
||||
|
||||
let used = 0
|
||||
let total = 0
|
||||
let unit = 'times'
|
||||
let trailProviderName = ''
|
||||
let hasFoundNotExhausted = false
|
||||
textGenerationModelList?.filter(({ model_provider: provider }) => {
|
||||
return provider.quota_type === 'trial'
|
||||
}).forEach(({ model_provider: provider }) => {
|
||||
if (hasFoundNotExhausted)
|
||||
return
|
||||
const { provider_name, quota_used, quota_limit, quota_unit } = provider
|
||||
if (quota_limit !== quota_used)
|
||||
hasFoundNotExhausted = true
|
||||
used = quota_used
|
||||
total = quota_limit
|
||||
unit = quota_unit
|
||||
trailProviderName = provider_name
|
||||
})
|
||||
|
||||
return [used, total, unit, trailProviderName]
|
||||
})()
|
||||
const usedPercent = Math.round(used / total * 100)
|
||||
const exhausted = isCloud && usedPercent === 100
|
||||
if (!(isShow))
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className={cn(exhausted ? 'bg-[#FEF3F2] border-[#FEE4E2]' : 'bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
|
||||
<div className={cn('bg-[#EFF4FF] border-[#D1E0FF]', 'mb-6 relative rounded-2xl shadow-md border p-8 ')}>
|
||||
<div className={cn('text-[24px] text-gray-800 font-semibold', isCloud ? 'flex items-center h-8 space-x-1' : 'leading-8 mb-6')}>
|
||||
{isCloud && <em-emoji id={exhausted ? '🤔' : '😀'} />}
|
||||
{isCloud && <em-emoji id={'😀'} />}
|
||||
{isCloud
|
||||
? (
|
||||
<div>{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.title`, { providerName: (ProviderConfig as any)[providerName as string]?.selector?.name[locale] || providerName })}</div>
|
||||
<div>{t('appOverview.apiKeyInfo.cloud.trial.title', { providerName: 'OpenAI' })}</div>
|
||||
)
|
||||
: (
|
||||
<div>
|
||||
|
|
@ -84,18 +41,7 @@ const APIKeyInfoPanel: FC = () => {
|
|||
)}
|
||||
</div>
|
||||
{isCloud && (
|
||||
<div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${exhausted ? 'exhausted' : 'trial'}.description`)}</div>
|
||||
)}
|
||||
{/* Call times info */}
|
||||
{isCloud && (
|
||||
<div className='my-5'>
|
||||
<div className='flex items-center h-5 space-x-2 text-sm text-gray-700 font-medium'>
|
||||
<div>{t(`appOverview.apiKeyInfo.${unit === 'times' ? 'callTimes' : 'usedToken'}`)}</div>
|
||||
<div>·</div>
|
||||
<div className={cn('font-semibold', exhausted && 'text-[#D92D20]')}>{formatNumber(used)}/{formatNumber(total)}</div>
|
||||
</div>
|
||||
<Progress className='mt-2' value={usedPercent} />
|
||||
</div>
|
||||
<div className='mt-1 text-sm text-gray-600 font-normal'>{t(`appOverview.apiKeyInfo.cloud.${'trial'}.description`)}</div>
|
||||
)}
|
||||
<Button
|
||||
type='primary'
|
||||
|
|
|
|||
31
web/app/components/base/button/index.css
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
@tailwind components;
|
||||
|
||||
@layer components {
|
||||
.btn {
|
||||
@apply inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base cursor-pointer;
|
||||
}
|
||||
|
||||
.btn-default {
|
||||
@apply border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300;
|
||||
}
|
||||
|
||||
.btn-default-disabled {
|
||||
@apply border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
@apply bg-primary-600 hover:bg-primary-600/75 cursor-pointer text-white hover:shadow-sm;
|
||||
}
|
||||
|
||||
.btn-primary-disabled {
|
||||
@apply bg-primary-200 cursor-not-allowed text-white;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
@apply bg-red-600 hover:bg-red-600/75 cursor-pointer text-white hover:shadow-sm;
|
||||
}
|
||||
|
||||
.btn-warning-disabled {
|
||||
@apply bg-red-600/75 cursor-not-allowed text-white;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,19 +24,19 @@ const Button: FC<IButtonProps> = ({
|
|||
let style = 'cursor-pointer'
|
||||
switch (type) {
|
||||
case 'primary':
|
||||
style = (disabled || loading) ? 'bg-primary-200 cursor-not-allowed text-white' : 'bg-primary-600 hover:bg-primary-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
|
||||
style = (disabled || loading) ? 'btn-primary-disabled' : 'btn-primary'
|
||||
break
|
||||
case 'warning':
|
||||
style = (disabled || loading) ? 'bg-red-600/75 cursor-not-allowed text-white' : 'bg-red-600 hover:bg-red-600/75 hover:shadow-md cursor-pointer text-white hover:shadow-sm'
|
||||
style = (disabled || loading) ? 'btn-warning-disabled' : 'btn-warning'
|
||||
break
|
||||
default:
|
||||
style = disabled ? 'border-solid border border-gray-200 bg-gray-200 cursor-not-allowed text-gray-800' : 'border-solid border border-gray-200 cursor-pointer text-gray-500 hover:bg-white hover:shadow-sm hover:border-gray-300'
|
||||
style = disabled ? 'btn-default-disabled' : 'btn-default'
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`inline-flex justify-center items-center content-center h-9 leading-5 rounded-lg px-4 py-2 text-base ${style} ${className && className}`}
|
||||
className={`btn ${style} ${className && className}`}
|
||||
tabIndex={tabIndex}
|
||||
onClick={disabled ? undefined : onClick}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="alert-triangle">
|
||||
<path id="Icon" d="M7.99977 5.33314V7.99981M7.99977 10.6665H8.00644M6.85977 1.90648L1.2131 11.3331C1.09668 11.5348 1.03508 11.7633 1.03443 11.9962C1.03378 12.229 1.0941 12.4579 1.20939 12.6602C1.32468 12.8624 1.49092 13.031 1.69157 13.149C1.89223 13.2671 2.1203 13.3306 2.3531 13.3331H13.6464C13.8792 13.3306 14.1073 13.2671 14.308 13.149C14.5086 13.031 14.6749 12.8624 14.7902 12.6602C14.9054 12.4579 14.9658 12.229 14.9651 11.9962C14.9645 11.7633 14.9029 11.5348 14.7864 11.3331L9.13977 1.90648C9.02092 1.71055 8.85358 1.54856 8.6539 1.43613C8.45422 1.32371 8.22893 1.26465 7.99977 1.26465C7.77061 1.26465 7.54532 1.32371 7.34564 1.43613C7.14596 1.54856 6.97862 1.71055 6.85977 1.90648Z" stroke="#F79009" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 908 B |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="coins-stacked-01">
|
||||
<path id="Icon" d="M12 17C12 19.7614 14.2386 22 17 22C19.7614 22 22 19.7614 22 17C22 14.2386 19.7614 12 17 12C14.2386 12 12 14.2386 12 17ZM12 17C12 15.8742 12.3721 14.8353 13 13.9995V5M12 17C12 17.8254 12.2 18.604 12.5541 19.2901C11.7117 20.0018 9.76584 20.5 7.5 20.5C4.46243 20.5 2 19.6046 2 18.5V5M13 5C13 6.10457 10.5376 7 7.5 7C4.46243 7 2 6.10457 2 5M13 5C13 3.89543 10.5376 3 7.5 3C4.46243 3 2 3.89543 2 5M2 14C2 15.1046 4.46243 16 7.5 16C9.689 16 11.5793 15.535 12.4646 14.8618M13 9.5C13 10.6046 10.5376 11.5 7.5 11.5C4.46243 11.5 2 10.6046 2 9.5" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 770 B |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="plus-circle">
|
||||
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1ZM12 7C12.5523 7 13 7.44772 13 8V11H16C16.5523 11 17 11.4477 17 12C17 12.5523 16.5523 13 16 13H13V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V13H8C7.44772 13 7 12.5523 7 12C7 11.4477 7.44772 11 8 11H11V8C11 7.44772 11.4477 7 12 7Z" fill="black"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 562 B |
|
|
@ -0,0 +1,9 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="box-sparkle, magic box">
|
||||
<g id="Icon">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.76205 2.07424C9.99723 2.21897 10.0706 2.52694 9.92583 2.76212L8.85632 4.50007H9.5C9.77614 4.50007 10 4.72393 10 5.00007V9.00007C10 10.1046 9.10457 11.0001 8 11.0001H4C2.89543 11.0001 2 10.1046 2 9.00007V5.00007C2 4.72393 2.22386 4.50007 2.5 4.50007H7.68214L9.07417 2.23802C9.2189 2.00284 9.52687 1.92952 9.76205 2.07424ZM5 6.50007C4.72386 6.50007 4.5 6.72393 4.5 7.00007C4.5 7.27621 4.72386 7.50007 5 7.50007H7C7.27614 7.50007 7.5 7.27621 7.5 7.00007C7.5 6.72393 7.27614 6.50007 7 6.50007H5Z" fill="#667085"/>
|
||||
<path d="M5.92504 1.53733C5.97342 1.51314 6.01265 1.47391 6.03684 1.42553L6.27597 0.947279C6.3681 0.763016 6.63105 0.763017 6.72318 0.947279L6.96231 1.42553C6.9865 1.47391 7.02573 1.51314 7.07411 1.53733L7.55236 1.77646C7.73663 1.86859 7.73663 2.13154 7.55236 2.22367L7.07411 2.4628C7.02573 2.48699 6.9865 2.52622 6.96231 2.5746L6.72318 3.05285C6.63105 3.23711 6.3681 3.23711 6.27597 3.05285L6.03684 2.5746C6.01265 2.52622 5.97342 2.48699 5.92504 2.4628L5.44679 2.22367C5.26253 2.13154 5.26253 1.86859 5.44679 1.77646L5.92504 1.53733Z" fill="#667085"/>
|
||||
<path d="M3.25837 2.37067C3.30676 2.34648 3.34599 2.30724 3.37018 2.25886L3.52597 1.94728C3.6181 1.76302 3.88105 1.76302 3.97318 1.94728L4.12898 2.25886C4.15317 2.30724 4.1924 2.34648 4.24078 2.37067L4.55236 2.52646C4.73662 2.61859 4.73663 2.88154 4.55236 2.97367L4.24078 3.12946C4.1924 3.15365 4.15317 3.19289 4.12898 3.24127L3.97318 3.55285C3.88105 3.73711 3.6181 3.73711 3.52597 3.55285L3.37018 3.24127C3.34599 3.19289 3.30676 3.15365 3.25837 3.12946L2.94679 2.97367C2.76253 2.88154 2.76253 2.61859 2.94679 2.52646L3.25837 2.37067Z" fill="#667085"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="eye-sparkle, magic eyes">
|
||||
<path id="Icon" fill-rule="evenodd" clip-rule="evenodd" d="M11.0338 5.05688C9.75366 3.05335 7.90203 1.99999 6.00017 2C4.09831 2.00001 2.24669 3.05341 0.966566 5.05693C0.599687 5.63113 0.599686 6.36892 0.966566 6.94312C2.24669 8.94665 4.09832 10 6.00018 10C7.90204 9.99999 9.75366 8.94659 11.0338 6.94307C11.4007 6.36887 11.4007 5.63108 11.0338 5.05688ZM5.77639 4.44721L5.3706 5.2588C5.34641 5.30718 5.30718 5.34641 5.2588 5.3706L4.44721 5.77639C4.26295 5.86852 4.26295 6.13148 4.44721 6.22361L5.2588 6.6294C5.30718 6.65359 5.34641 6.69282 5.3706 6.7412L5.77639 7.55279C5.86852 7.73705 6.13148 7.73705 6.22361 7.55279L6.6294 6.7412C6.65359 6.69282 6.69282 6.65359 6.7412 6.6294L7.55279 6.22361C7.73705 6.13148 7.73705 5.86852 7.55279 5.77639L6.7412 5.3706C6.69282 5.34641 6.65359 5.30718 6.6294 5.2588L6.22361 4.44721C6.13148 4.26295 5.86852 4.26295 5.77639 4.44721Z" fill="#667085"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1 KiB |
|
|
@ -0,0 +1,10 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="magic-wand-2, magic stick, star">
|
||||
<g id="Icon">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.27056 1.77151C8.811 1.23107 9.68723 1.23107 10.2277 1.77151C10.7681 2.31195 10.7681 3.18818 10.2277 3.72862L3.72767 10.2286C3.18723 10.7691 2.31101 10.7691 1.77056 10.2286C1.23012 9.68818 1.23012 8.81195 1.77056 8.27151L8.27056 1.77151ZM9.52056 2.47862C9.37065 2.3287 9.12759 2.3287 8.97767 2.47862L8.08122 3.37506L8.62412 3.91796L9.52056 3.02151C9.67048 2.87159 9.67048 2.62853 9.52056 2.47862Z" fill="#667085"/>
|
||||
<path d="M4.92504 1.03733C4.97342 1.01314 5.01265 0.973911 5.03684 0.92553L5.27597 0.447279C5.3681 0.263016 5.63105 0.263017 5.72318 0.447279L5.96231 0.92553C5.9865 0.973911 6.02573 1.01314 6.07411 1.03733L6.55236 1.27646C6.73663 1.36859 6.73663 1.63154 6.55236 1.72367L6.07411 1.9628C6.02573 1.98699 5.9865 2.02622 5.96231 2.0746L5.72318 2.55285C5.63105 2.73711 5.3681 2.73711 5.27597 2.55285L5.03684 2.0746C5.01265 2.02622 4.97342 1.98699 4.92504 1.9628L4.44679 1.72367C4.26253 1.63154 4.26253 1.36859 4.44679 1.27646L4.92504 1.03733Z" fill="#667085"/>
|
||||
<path d="M9.42504 6.53733C9.47342 6.51314 9.51265 6.47391 9.53684 6.42553L9.77597 5.94728C9.8681 5.76302 10.1311 5.76302 10.2232 5.94728L10.4623 6.42553C10.4865 6.47391 10.5257 6.51314 10.5741 6.53733L11.0524 6.77646C11.2366 6.86859 11.2366 7.13154 11.0524 7.22367L10.5741 7.4628C10.5257 7.48699 10.4865 7.52622 10.4623 7.5746L10.2232 8.05285C10.1311 8.23711 9.8681 8.23711 9.77597 8.05285L9.53684 7.5746C9.51265 7.52622 9.47342 7.48699 9.42504 7.4628L8.94679 7.22367C8.76253 7.13154 8.76253 6.86859 8.94679 6.77646L9.42504 6.53733Z" fill="#667085"/>
|
||||
<path d="M2.42504 3.53733C2.47342 3.51314 2.51265 3.47391 2.53684 3.42553L2.77597 2.94728C2.8681 2.76302 3.13105 2.76302 3.22318 2.94728L3.46231 3.42553C3.4865 3.47391 3.52573 3.51314 3.57411 3.53733L4.05236 3.77646C4.23663 3.86859 4.23663 4.13154 4.05236 4.22367L3.57411 4.4628C3.52573 4.48699 3.4865 4.52622 3.46231 4.5746L3.22318 5.05285C3.13105 5.23711 2.8681 5.23711 2.77597 5.05285L2.53684 4.5746C2.51265 4.52622 2.47342 4.48699 2.42504 4.4628L1.94679 4.22367C1.76253 4.13154 1.76253 3.86859 1.94679 3.77646L2.42504 3.53733Z" fill="#667085"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="robot, bot">
|
||||
<path id="Icon" fill-rule="evenodd" clip-rule="evenodd" d="M6 0.5C6.27614 0.5 6.5 0.723858 6.5 1V1.5H8.5C9.32843 1.5 10 2.17157 10 3V5.5C10 5.94425 9.80688 6.34339 9.5 6.61805V7.29289L10.3536 8.14645C10.5488 8.34171 10.5488 8.65829 10.3536 8.85355C10.1583 9.04882 9.84171 9.04882 9.64645 8.85355L9.34052 8.54762C8.89526 9.96884 7.56805 11 6 11C4.43195 11 3.10474 9.96884 2.65948 8.54762L2.35355 8.85355C2.15829 9.04882 1.84171 9.04882 1.64645 8.85355C1.45118 8.65829 1.45118 8.34171 1.64645 8.14645L2.5 7.29289V6.61805C2.19313 6.34339 2 5.94425 2 5.5V3C2 2.17157 2.67157 1.5 3.5 1.5H5.5V1C5.5 0.723858 5.72386 0.5 6 0.5ZM3.5 2.5C3.22386 2.5 3 2.72386 3 3V5.5C3 5.77614 3.22386 6 3.5 6H8.5C8.77614 6 9 5.77614 9 5.5V3C9 2.72386 8.77614 2.5 8.5 2.5H3.5ZM4.5 3.5C4.77614 3.5 5 3.72386 5 4V4.5C5 4.77614 4.77614 5 4.5 5C4.22386 5 4 4.77614 4 4.5V4C4 3.72386 4.22386 3.5 4.5 3.5ZM7.5 3.5C7.77614 3.5 8 3.72386 8 4V4.5C8 4.77614 7.77614 5 7.5 5C7.22386 5 7 4.77614 7 4.5V4C7 3.72386 7.22386 3.5 7.5 3.5Z" fill="#667085"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "16",
|
||||
"height": "16",
|
||||
"viewBox": "0 0 16 16",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "alert-triangle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M7.99977 5.33314V7.99981M7.99977 10.6665H8.00644M6.85977 1.90648L1.2131 11.3331C1.09668 11.5348 1.03508 11.7633 1.03443 11.9962C1.03378 12.229 1.0941 12.4579 1.20939 12.6602C1.32468 12.8624 1.49092 13.031 1.69157 13.149C1.89223 13.2671 2.1203 13.3306 2.3531 13.3331H13.6464C13.8792 13.3306 14.1073 13.2671 14.308 13.149C14.5086 13.031 14.6749 12.8624 14.7902 12.6602C14.9054 12.4579 14.9658 12.229 14.9651 11.9962C14.9645 11.7633 14.9029 11.5348 14.7864 11.3331L9.13977 1.90648C9.02092 1.71055 8.85358 1.54856 8.6539 1.43613C8.45422 1.32371 8.22893 1.26465 7.99977 1.26465C7.77061 1.26465 7.54532 1.32371 7.34564 1.43613C7.14596 1.54856 6.97862 1.71055 6.85977 1.90648Z",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "1.25",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "AlertTriangle"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './AlertTriangle.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'AlertTriangle'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -1 +1,2 @@
|
|||
export { default as AlertCircle } from './AlertCircle'
|
||||
export { default as AlertTriangle } from './AlertTriangle'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "coins-stacked-01"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"d": "M12 17C12 19.7614 14.2386 22 17 22C19.7614 22 22 19.7614 22 17C22 14.2386 19.7614 12 17 12C14.2386 12 12 14.2386 12 17ZM12 17C12 15.8742 12.3721 14.8353 13 13.9995V5M12 17C12 17.8254 12.2 18.604 12.5541 19.2901C11.7117 20.0018 9.76584 20.5 7.5 20.5C4.46243 20.5 2 19.6046 2 18.5V5M13 5C13 6.10457 10.5376 7 7.5 7C4.46243 7 2 6.10457 2 5M13 5C13 3.89543 10.5376 3 7.5 3C4.46243 3 2 3.89543 2 5M2 14C2 15.1046 4.46243 16 7.5 16C9.689 16 11.5793 15.535 12.4646 14.8618M13 9.5C13 10.6046 10.5376 11.5 7.5 11.5C4.46243 11.5 2 10.6046 2 9.5",
|
||||
"stroke": "currentColor",
|
||||
"stroke-width": "2",
|
||||
"stroke-linecap": "round",
|
||||
"stroke-linejoin": "round"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "CoinsStacked01"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './CoinsStacked01.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'CoinsStacked01'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
export { default as CoinsStacked01 } from './CoinsStacked01'
|
||||
export { default as GoldCoin } from './GoldCoin'
|
||||
export { default as ReceiptList } from './ReceiptList'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "24",
|
||||
"height": "24",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "plus-circle"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Solid",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M12 1C5.92487 1 1 5.92487 1 12C1 18.0751 5.92487 23 12 23C18.0751 23 23 18.0751 23 12C23 5.92487 18.0751 1 12 1ZM12 7C12.5523 7 13 7.44772 13 8V11H16C16.5523 11 17 11.4477 17 12C17 12.5523 16.5523 13 16 13H13V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V13H8C7.44772 13 7 12.5523 7 12C7 11.4477 7.44772 11 8 11H11V8C11 7.44772 11.4477 7 12 7Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "PlusCircle"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './PlusCircle.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'PlusCircle'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -4,6 +4,7 @@ export { default as Download02 } from './Download02'
|
|||
export { default as Edit04 } from './Edit04'
|
||||
export { default as Eye } from './Eye'
|
||||
export { default as MessageClockCircle } from './MessageClockCircle'
|
||||
export { default as PlusCircle } from './PlusCircle'
|
||||
export { default as Target04 } from './Target04'
|
||||
export { default as Tool03 } from './Tool03'
|
||||
export { default as XCircle } from './XCircle'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "box-sparkle, magic box"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M9.76205 2.07424C9.99723 2.21897 10.0706 2.52694 9.92583 2.76212L8.85632 4.50007H9.5C9.77614 4.50007 10 4.72393 10 5.00007V9.00007C10 10.1046 9.10457 11.0001 8 11.0001H4C2.89543 11.0001 2 10.1046 2 9.00007V5.00007C2 4.72393 2.22386 4.50007 2.5 4.50007H7.68214L9.07417 2.23802C9.2189 2.00284 9.52687 1.92952 9.76205 2.07424ZM5 6.50007C4.72386 6.50007 4.5 6.72393 4.5 7.00007C4.5 7.27621 4.72386 7.50007 5 7.50007H7C7.27614 7.50007 7.5 7.27621 7.5 7.00007C7.5 6.72393 7.27614 6.50007 7 6.50007H5Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M5.92504 1.53733C5.97342 1.51314 6.01265 1.47391 6.03684 1.42553L6.27597 0.947279C6.3681 0.763016 6.63105 0.763017 6.72318 0.947279L6.96231 1.42553C6.9865 1.47391 7.02573 1.51314 7.07411 1.53733L7.55236 1.77646C7.73663 1.86859 7.73663 2.13154 7.55236 2.22367L7.07411 2.4628C7.02573 2.48699 6.9865 2.52622 6.96231 2.5746L6.72318 3.05285C6.63105 3.23711 6.3681 3.23711 6.27597 3.05285L6.03684 2.5746C6.01265 2.52622 5.97342 2.48699 5.92504 2.4628L5.44679 2.22367C5.26253 2.13154 5.26253 1.86859 5.44679 1.77646L5.92504 1.53733Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M3.25837 2.37067C3.30676 2.34648 3.34599 2.30724 3.37018 2.25886L3.52597 1.94728C3.6181 1.76302 3.88105 1.76302 3.97318 1.94728L4.12898 2.25886C4.15317 2.30724 4.1924 2.34648 4.24078 2.37067L4.55236 2.52646C4.73662 2.61859 4.73663 2.88154 4.55236 2.97367L4.24078 3.12946C4.1924 3.15365 4.15317 3.19289 4.12898 3.24127L3.97318 3.55285C3.88105 3.73711 3.6181 3.73711 3.52597 3.55285L3.37018 3.24127C3.34599 3.19289 3.30676 3.15365 3.25837 3.12946L2.94679 2.97367C2.76253 2.88154 2.76253 2.61859 2.94679 2.52646L3.25837 2.37067Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "MagicBox"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './MagicBox.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'MagicBox'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "eye-sparkle, magic eyes"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M11.0338 5.05688C9.75366 3.05335 7.90203 1.99999 6.00017 2C4.09831 2.00001 2.24669 3.05341 0.966566 5.05693C0.599687 5.63113 0.599686 6.36892 0.966566 6.94312C2.24669 8.94665 4.09832 10 6.00018 10C7.90204 9.99999 9.75366 8.94659 11.0338 6.94307C11.4007 6.36887 11.4007 5.63108 11.0338 5.05688ZM5.77639 4.44721L5.3706 5.2588C5.34641 5.30718 5.30718 5.34641 5.2588 5.3706L4.44721 5.77639C4.26295 5.86852 4.26295 6.13148 4.44721 6.22361L5.2588 6.6294C5.30718 6.65359 5.34641 6.69282 5.3706 6.7412L5.77639 7.55279C5.86852 7.73705 6.13148 7.73705 6.22361 7.55279L6.6294 6.7412C6.65359 6.69282 6.69282 6.65359 6.7412 6.6294L7.55279 6.22361C7.73705 6.13148 7.73705 5.86852 7.55279 5.77639L6.7412 5.3706C6.69282 5.34641 6.65359 5.30718 6.6294 5.2588L6.22361 4.44721C6.13148 4.26295 5.86852 4.26295 5.77639 4.44721Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "MagicEyes"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './MagicEyes.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'MagicEyes'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "magic-wand-2, magic stick, star"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "Icon"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M8.27056 1.77151C8.811 1.23107 9.68723 1.23107 10.2277 1.77151C10.7681 2.31195 10.7681 3.18818 10.2277 3.72862L3.72767 10.2286C3.18723 10.7691 2.31101 10.7691 1.77056 10.2286C1.23012 9.68818 1.23012 8.81195 1.77056 8.27151L8.27056 1.77151ZM9.52056 2.47862C9.37065 2.3287 9.12759 2.3287 8.97767 2.47862L8.08122 3.37506L8.62412 3.91796L9.52056 3.02151C9.67048 2.87159 9.67048 2.62853 9.52056 2.47862Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M4.92504 1.03733C4.97342 1.01314 5.01265 0.973911 5.03684 0.92553L5.27597 0.447279C5.3681 0.263016 5.63105 0.263017 5.72318 0.447279L5.96231 0.92553C5.9865 0.973911 6.02573 1.01314 6.07411 1.03733L6.55236 1.27646C6.73663 1.36859 6.73663 1.63154 6.55236 1.72367L6.07411 1.9628C6.02573 1.98699 5.9865 2.02622 5.96231 2.0746L5.72318 2.55285C5.63105 2.73711 5.3681 2.73711 5.27597 2.55285L5.03684 2.0746C5.01265 2.02622 4.97342 1.98699 4.92504 1.9628L4.44679 1.72367C4.26253 1.63154 4.26253 1.36859 4.44679 1.27646L4.92504 1.03733Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M9.42504 6.53733C9.47342 6.51314 9.51265 6.47391 9.53684 6.42553L9.77597 5.94728C9.8681 5.76302 10.1311 5.76302 10.2232 5.94728L10.4623 6.42553C10.4865 6.47391 10.5257 6.51314 10.5741 6.53733L11.0524 6.77646C11.2366 6.86859 11.2366 7.13154 11.0524 7.22367L10.5741 7.4628C10.5257 7.48699 10.4865 7.52622 10.4623 7.5746L10.2232 8.05285C10.1311 8.23711 9.8681 8.23711 9.77597 8.05285L9.53684 7.5746C9.51265 7.52622 9.47342 7.48699 9.42504 7.4628L8.94679 7.22367C8.76253 7.13154 8.76253 6.86859 8.94679 6.77646L9.42504 6.53733Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M2.42504 3.53733C2.47342 3.51314 2.51265 3.47391 2.53684 3.42553L2.77597 2.94728C2.8681 2.76302 3.13105 2.76302 3.22318 2.94728L3.46231 3.42553C3.4865 3.47391 3.52573 3.51314 3.57411 3.53733L4.05236 3.77646C4.23663 3.86859 4.23663 4.13154 4.05236 4.22367L3.57411 4.4628C3.52573 4.48699 3.4865 4.52622 3.46231 4.5746L3.22318 5.05285C3.13105 5.23711 2.8681 5.23711 2.77597 5.05285L2.53684 4.5746C2.51265 4.52622 2.47342 4.48699 2.42504 4.4628L1.94679 4.22367C1.76253 4.13154 1.76253 3.86859 1.94679 3.77646L2.42504 3.53733Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "MagicWand"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './MagicWand.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'MagicWand'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"width": "12",
|
||||
"height": "12",
|
||||
"viewBox": "0 0 12 12",
|
||||
"fill": "none",
|
||||
"xmlns": "http://www.w3.org/2000/svg"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "g",
|
||||
"attributes": {
|
||||
"id": "robot, bot"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"id": "Icon",
|
||||
"fill-rule": "evenodd",
|
||||
"clip-rule": "evenodd",
|
||||
"d": "M6 0.5C6.27614 0.5 6.5 0.723858 6.5 1V1.5H8.5C9.32843 1.5 10 2.17157 10 3V5.5C10 5.94425 9.80688 6.34339 9.5 6.61805V7.29289L10.3536 8.14645C10.5488 8.34171 10.5488 8.65829 10.3536 8.85355C10.1583 9.04882 9.84171 9.04882 9.64645 8.85355L9.34052 8.54762C8.89526 9.96884 7.56805 11 6 11C4.43195 11 3.10474 9.96884 2.65948 8.54762L2.35355 8.85355C2.15829 9.04882 1.84171 9.04882 1.64645 8.85355C1.45118 8.65829 1.45118 8.34171 1.64645 8.14645L2.5 7.29289V6.61805C2.19313 6.34339 2 5.94425 2 5.5V3C2 2.17157 2.67157 1.5 3.5 1.5H5.5V1C5.5 0.723858 5.72386 0.5 6 0.5ZM3.5 2.5C3.22386 2.5 3 2.72386 3 3V5.5C3 5.77614 3.22386 6 3.5 6H8.5C8.77614 6 9 5.77614 9 5.5V3C9 2.72386 8.77614 2.5 8.5 2.5H3.5ZM4.5 3.5C4.77614 3.5 5 3.72386 5 4V4.5C5 4.77614 4.77614 5 4.5 5C4.22386 5 4 4.77614 4 4.5V4C4 3.72386 4.22386 3.5 4.5 3.5ZM7.5 3.5C7.77614 3.5 8 3.72386 8 4V4.5C8 4.77614 7.77614 5 7.5 5C7.22386 5 7 4.77614 7 4.5V4C7 3.72386 7.22386 3.5 7.5 3.5Z",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Robot"
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Robot.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Robot'
|
||||
|
||||
export default Icon
|
||||
|
|
@ -1,3 +1,7 @@
|
|||
export { default as MagicBox } from './MagicBox'
|
||||
export { default as MagicEyes } from './MagicEyes'
|
||||
export { default as MagicWand } from './MagicWand'
|
||||
export { default as Microphone01 } from './Microphone01'
|
||||
export { default as Robot } from './Robot'
|
||||
export { default as Sliders02 } from './Sliders02'
|
||||
export { default as StopCircle } from './StopCircle'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ const PromptLogModal: FC<PromptLogModalProps> = ({
|
|||
setMounted(true)
|
||||
}, [])
|
||||
|
||||
if (!log)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div
|
||||
className='fixed top-16 left-2 bottom-2 flex flex-col bg-white border-[0.5px] border-gray-200 rounded-xl shadow-xl z-10'
|
||||
|
|
@ -37,7 +40,7 @@ const PromptLogModal: FC<PromptLogModalProps> = ({
|
|||
<div className='text-base font-semibold text-gray-900'>PROMPT LOG</div>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
log.length === 1 && (
|
||||
log?.length === 1 && (
|
||||
<>
|
||||
<CopyFeedbackNew className='w-6 h-6' content={log[0].text} />
|
||||
<div className='mx-2.5 w-[1px] h-[14px] bg-gray-200' />
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@
|
|||
}
|
||||
|
||||
.slider-thumb {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
top: -8px;
|
||||
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.08);
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ const Toast = ({
|
|||
|
||||
return <div className={classNames(
|
||||
className,
|
||||
'fixed rounded-md p-4 my-4 mx-8 z-50',
|
||||
'fixed rounded-md p-4 my-4 mx-8 z-[9999]',
|
||||
'top-0',
|
||||
'right-0',
|
||||
type === 'success' ? 'bg-green-50' : '',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import type { BackendModel } from '../../header/account-setting/model-page/declarations'
|
||||
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
||||
import type {
|
||||
DefaultModelResponse,
|
||||
Model,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/declarations'
|
||||
|
||||
export const isReRankModelSelected = ({
|
||||
rerankDefaultModel,
|
||||
|
|
@ -8,15 +11,18 @@ export const isReRankModelSelected = ({
|
|||
rerankModelList,
|
||||
indexMethod,
|
||||
}: {
|
||||
rerankDefaultModel?: BackendModel
|
||||
rerankDefaultModel?: DefaultModelResponse
|
||||
isRerankDefaultModelVaild: boolean
|
||||
retrievalConfig: RetrievalConfig
|
||||
rerankModelList: BackendModel[]
|
||||
rerankModelList: Model[]
|
||||
indexMethod?: string
|
||||
}) => {
|
||||
const rerankModelSelected = (() => {
|
||||
if (retrievalConfig.reranking_model?.reranking_model_name)
|
||||
return !!rerankModelList.find(({ model_name }) => model_name === retrievalConfig.reranking_model?.reranking_model_name)
|
||||
if (retrievalConfig.reranking_model?.reranking_model_name) {
|
||||
const provider = rerankModelList.find(({ provider }) => provider === retrievalConfig.reranking_model?.reranking_provider_name)
|
||||
|
||||
return provider?.models.find(({ model }) => model === retrievalConfig.reranking_model?.reranking_model_name)
|
||||
}
|
||||
|
||||
if (isRerankDefaultModelVaild)
|
||||
return !!rerankDefaultModel
|
||||
|
|
@ -39,7 +45,7 @@ export const ensureRerankModelSelected = ({
|
|||
indexMethod,
|
||||
retrievalConfig,
|
||||
}: {
|
||||
rerankDefaultModel: BackendModel
|
||||
rerankDefaultModel: DefaultModelResponse
|
||||
retrievalConfig: RetrievalConfig
|
||||
indexMethod?: string
|
||||
}) => {
|
||||
|
|
@ -52,8 +58,8 @@ export const ensureRerankModelSelected = ({
|
|||
return {
|
||||
...retrievalConfig,
|
||||
reranking_model: {
|
||||
reranking_provider_name: rerankDefaultModel.model_provider.provider_name,
|
||||
reranking_model_name: rerankDefaultModel.model_name,
|
||||
reranking_provider_name: rerankDefaultModel.provider.provider,
|
||||
reranking_model_name: rerankDefaultModel.model,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import RadioCard from '@/app/components/base/radio-card'
|
|||
import { PatternRecognition, Semantic } from '@/app/components/base/icons/src/vender/solid/development'
|
||||
import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type Props = {
|
||||
value: RetrievalConfig
|
||||
|
|
@ -20,14 +21,15 @@ const RetrievalMethodConfig: FC<Props> = ({
|
|||
onChange,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { supportRetrievalMethods, rerankDefaultModel } = useProviderContext()
|
||||
const { supportRetrievalMethods } = useProviderContext()
|
||||
const { data: rerankDefaultModel } = useDefaultModel(3)
|
||||
const value = (() => {
|
||||
if (!passValue.reranking_model.reranking_model_name) {
|
||||
return {
|
||||
...passValue,
|
||||
reranking_model: {
|
||||
reranking_provider_name: rerankDefaultModel?.model_provider.provider_name || '',
|
||||
reranking_model_name: rerankDefaultModel?.model_name || '',
|
||||
reranking_provider_name: rerankDefaultModel?.provider.provider || '',
|
||||
reranking_model_name: rerankDefaultModel?.model || '',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,9 @@ import { RETRIEVE_METHOD } from '@/types/app'
|
|||
import Switch from '@/app/components/base/switch'
|
||||
import Tooltip from '@/app/components/base/tooltip-plus'
|
||||
import { HelpCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import type { RetrievalConfig } from '@/types/app'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useModelListAndDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type Props = {
|
||||
type: RETRIEVE_METHOD
|
||||
|
|
@ -29,8 +28,9 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||
const canToggleRerankModalEnable = type !== RETRIEVE_METHOD.hybrid
|
||||
const isEconomical = type === RETRIEVE_METHOD.invertedIndex
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
} = useProviderContext()
|
||||
defaultModel: rerankDefaultModel,
|
||||
modelList: rerankModelList,
|
||||
} = useModelListAndDefaultModel(3)
|
||||
|
||||
const rerankModel = (() => {
|
||||
if (value.reranking_model) {
|
||||
|
|
@ -41,8 +41,8 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||
}
|
||||
else if (rerankDefaultModel) {
|
||||
return {
|
||||
provider_name: rerankDefaultModel.model_provider.provider_name,
|
||||
model_name: rerankDefaultModel.model_name,
|
||||
provider_name: rerankDefaultModel.provider.provider,
|
||||
model_name: rerankDefaultModel.model,
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
@ -71,24 +71,21 @@ const RetrievalParamConfig: FC<Props> = ({
|
|||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ModelSelector
|
||||
whenEmptyGoToSetting
|
||||
popClassName='!max-w-[100%] !w-full'
|
||||
value={rerankModel && { providerName: rerankModel.provider_name, modelName: rerankModel.model_name } as any}
|
||||
modelType={ModelType.reranking}
|
||||
readonly={!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid}
|
||||
onChange={(v) => {
|
||||
onChange({
|
||||
...value,
|
||||
reranking_model: {
|
||||
reranking_provider_name: v.model_provider.provider_name,
|
||||
reranking_model_name: v.model_name,
|
||||
},
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<ModelSelector
|
||||
triggerClassName={`${!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid && '!opacity-60 !cursor-not-allowed'}`}
|
||||
defaultModel={rerankModel && { provider: rerankModel.provider_name, model: rerankModel.model_name }}
|
||||
modelList={rerankModelList}
|
||||
readonly={!value.reranking_enable && type !== RETRIEVE_METHOD.hybrid}
|
||||
onSelect={(v) => {
|
||||
onChange({
|
||||
...value,
|
||||
reranking_model: {
|
||||
reranking_provider_name: v.provider,
|
||||
reranking_model_name: v.model,
|
||||
},
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import type { DataSet, FileItem, createDocumentResponse } from '@/models/dataset
|
|||
import { fetchDataSource } from '@/service/common'
|
||||
import { fetchDatasetDetail } from '@/service/datasets'
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type DatasetUpdateFormProps = {
|
||||
datasetId?: string
|
||||
|
|
@ -28,7 +28,7 @@ const DatasetUpdateForm = ({ datasetId }: DatasetUpdateFormProps) => {
|
|||
const [fileList, setFiles] = useState<FileItem[]>([])
|
||||
const [result, setResult] = useState<createDocumentResponse | undefined>()
|
||||
const [hasError, setHasError] = useState(false)
|
||||
const { embeddingsDefaultModel } = useProviderContext()
|
||||
const { data: embeddingsDefaultModel } = useDefaultModel(2)
|
||||
|
||||
const [notionPages, setNotionPages] = useState<NotionPage[]>([])
|
||||
const updateNotionPages = (value: NotionPage[]) => {
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@ import { useDatasetDetailContext } from '@/context/dataset-detail'
|
|||
import I18n from '@/context/i18n'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import { RETRIEVE_METHOD } from '@/types/app'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type ValueOf<T> = T[keyof T]
|
||||
type StepTwoProps = {
|
||||
|
|
@ -268,10 +268,10 @@ const StepTwo = ({
|
|||
}
|
||||
}
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
} = useProviderContext()
|
||||
modelList: rerankModelList,
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(3)
|
||||
const getCreationParams = () => {
|
||||
let params
|
||||
if (isSetting) {
|
||||
|
|
@ -289,7 +289,7 @@ const StepTwo = ({
|
|||
if (
|
||||
!isReRankModelSelected({
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
isRerankDefaultModelVaild: !!isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
retrievalConfig,
|
||||
|
|
@ -489,8 +489,8 @@ const StepTwo = ({
|
|||
search_method: RETRIEVE_METHOD.semantic,
|
||||
reranking_enable: false,
|
||||
reranking_model: {
|
||||
reranking_provider_name: rerankDefaultModel?.model_provider.provider_name,
|
||||
reranking_model_name: rerankDefaultModel?.model_name,
|
||||
reranking_provider_name: rerankDefaultModel?.provider.provider,
|
||||
reranking_model_name: rerankDefaultModel?.model,
|
||||
},
|
||||
top_k: 3,
|
||||
score_threshold_enabled: false,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import Loading from '@/app/components/base/loading'
|
|||
import StepTwo from '@/app/components/datasets/create/step-two'
|
||||
import AccountSetting from '@/app/components/header/account-setting'
|
||||
import AppUnavailable from '@/app/components/base/app-unavailable'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type DocumentSettingsProps = {
|
||||
datasetId: string
|
||||
|
|
@ -26,7 +26,7 @@ const DocumentSettings = ({ datasetId, documentId }: DocumentSettingsProps) => {
|
|||
const [isShowSetAPIKey, { setTrue: showSetAPIKey, setFalse: hideSetAPIkey }] = useBoolean()
|
||||
const [hasError, setHasError] = useState(false)
|
||||
const { indexingTechnique, dataset } = useContext(DatasetDetailContext)
|
||||
const { embeddingsDefaultModel } = useProviderContext()
|
||||
const { data: embeddingsDefaultModel } = useDefaultModel(2)
|
||||
|
||||
const saveHandler = () => router.push(`/datasets/${datasetId}/documents/${documentId}`)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ import type { RetrievalConfig } from '@/types/app'
|
|||
import RetrievalMethodConfig from '@/app/components/datasets/common/retrieval-method-config'
|
||||
import EconomicalRetrievalMethodConfig from '@/app/components/datasets/common/economical-retrieval-method-config'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
type Props = {
|
||||
indexMethod: string
|
||||
|
|
@ -36,16 +36,16 @@ const ModifyRetrievalModal: FC<Props> = ({
|
|||
// }, ref)
|
||||
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
} = useProviderContext()
|
||||
modelList: rerankModelList,
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(3)
|
||||
|
||||
const handleSave = () => {
|
||||
if (
|
||||
!isReRankModelSelected({
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
isRerankDefaultModelVaild: !!isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
retrievalConfig,
|
||||
indexMethod,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,16 @@ import { ToastContext } from '@/app/components/base/toast'
|
|||
import Button from '@/app/components/base/button'
|
||||
import { updateDatasetSetting } from '@/service/datasets'
|
||||
import type { DataSet, DataSetListResponse } from '@/models/datasets'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-page/model-selector'
|
||||
import type { ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { type RetrievalConfig } from '@/types/app'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { ensureRerankModelSelected, isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import {
|
||||
useModelList,
|
||||
useModelListAndDefaultModelAndCurrentProviderAndModel,
|
||||
} from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
const rowClass = `
|
||||
flex justify-between py-4 flex-wrap gap-y-2
|
||||
`
|
||||
|
|
@ -56,11 +58,13 @@ const Form = () => {
|
|||
const [permission, setPermission] = useState(currentDataset?.permission)
|
||||
const [indexMethod, setIndexMethod] = useState(currentDataset?.indexing_technique)
|
||||
const [retrievalConfig, setRetrievalConfig] = useState(currentDataset?.retrieval_model_dict as RetrievalConfig)
|
||||
|
||||
const {
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
} = useProviderContext()
|
||||
modelList: rerankModelList,
|
||||
defaultModel: rerankDefaultModel,
|
||||
currentModel: isRerankDefaultModelVaild,
|
||||
} = useModelListAndDefaultModelAndCurrentProviderAndModel(3)
|
||||
const { data: embeddingModelList } = useModelList(2)
|
||||
|
||||
const handleSave = async () => {
|
||||
if (loading)
|
||||
|
|
@ -72,7 +76,7 @@ const Form = () => {
|
|||
if (
|
||||
!isReRankModelSelected({
|
||||
rerankDefaultModel,
|
||||
isRerankDefaultModelVaild,
|
||||
isRerankDefaultModelVaild: !!isRerankDefaultModelVaild,
|
||||
rerankModelList,
|
||||
retrievalConfig,
|
||||
indexMethod,
|
||||
|
|
@ -183,17 +187,15 @@ const Form = () => {
|
|||
<div>{t('datasetSettings.form.embeddingModel')}</div>
|
||||
</div>
|
||||
<div className='w-[480px]'>
|
||||
<div className='w-full h-9 rounded-lg bg-gray-100 opacity-60'>
|
||||
<ModelSelector
|
||||
readonly
|
||||
value={{
|
||||
providerName: currentDataset.embedding_model_provider as ProviderEnum,
|
||||
modelName: currentDataset.embedding_model,
|
||||
}}
|
||||
modelType={ModelType.embeddings}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
<ModelSelector
|
||||
readonly
|
||||
triggerClassName='!h-9 !cursor-not-allowed opacity-60'
|
||||
defaultModel={{
|
||||
provider: currentDataset.embedding_model_provider,
|
||||
model: currentDataset.embedding_model,
|
||||
}}
|
||||
modelList={embeddingModelList}
|
||||
/>
|
||||
<div className='mt-2 w-full text-xs leading-6 text-gray-500'>
|
||||
{t('datasetSettings.form.embeddingModelTip')}
|
||||
<span className='text-[#155eef] cursor-pointer' onClick={() => setShowAccountSettingModal({ payload: 'provider' })}>{t('datasetSettings.form.embeddingModelTipLink')}</span>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ const CreateAppModal = ({
|
|||
</Modal>
|
||||
{showEmojiPicker && <EmojiPicker
|
||||
onSelect={(icon, icon_background) => {
|
||||
console.log(icon, icon_background)
|
||||
setEmoji({ icon, icon_background })
|
||||
setShowEmojiPicker(false)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,11 @@ 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'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
|
||||
type Props = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
providerName: string
|
||||
plugins: Record<string, boolean>
|
||||
dataSets: DataSet[]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@ import React from 'react'
|
|||
import cn from 'classnames'
|
||||
import { useBoolean, useClickAway } from 'ahooks'
|
||||
import s from './style.module.css'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
|
||||
import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
|
||||
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'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { useAgentThoughtCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
|
||||
export type ISummaryProps = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
providerName: string
|
||||
plugins: Record<string, boolean>
|
||||
dataSets: DataSet[]
|
||||
}
|
||||
|
|
@ -49,9 +48,12 @@ const Summary: FC<ISummaryProps> = ({
|
|||
plugins,
|
||||
dataSets,
|
||||
}) => {
|
||||
const { agentThoughtModelList } = useProviderContext()
|
||||
const currModel = agentThoughtModelList.find(item => item.model_name === modelId && item.model_provider.provider_name === providerName)
|
||||
|
||||
const {
|
||||
currentModel: currModel,
|
||||
currentProvider,
|
||||
} = useAgentThoughtCurrentProviderAndModelAndModelList(
|
||||
{ provider: providerName, model: modelId },
|
||||
)
|
||||
// current_datetime is not configable and do not have icon
|
||||
const pluginIds = Object.keys(plugins).filter(key => plugins[key] && key !== 'current_datetime')
|
||||
const [isShowConfig, { setFalse: hideConfig, toggle: toggleShowConfig }] = useBoolean(false)
|
||||
|
|
@ -63,8 +65,16 @@ 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 providerName={providerName} modelId={modelId} className='!w-6 !h-6' />
|
||||
<div className='ml-2 text-[13px] font-medium text-gray-900'><ModelName modelId={modelId} modelDisplayName={currModel?.model_display_name} /></div>
|
||||
<ModelIcon
|
||||
provider={currentProvider}
|
||||
modelName={currModel?.model}
|
||||
className='!w-6 !h-6'
|
||||
/>
|
||||
<div className='ml-2 text-[13px] font-medium text-gray-900'>
|
||||
<ModelName
|
||||
modelItem={currModel!}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
pluginIds.length > 0 && (
|
||||
<div className='ml-1.5 flex items-center'>
|
||||
|
|
|
|||
|
|
@ -4,15 +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'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
|
||||
export type IConfigProps = {
|
||||
className?: string
|
||||
readonly?: boolean
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
onModelChange?: (modelId: string, providerName: ProviderEnum) => void
|
||||
providerName: string
|
||||
onModelChange?: (modelId: string, providerName: string) => void
|
||||
plugins: Record<string, boolean>
|
||||
onPluginChange?: (key: string, value: boolean) => void
|
||||
dataSets: DataSet[]
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
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'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
export type IModelConfigProps = {
|
||||
modelId: string
|
||||
providerName: ProviderEnum
|
||||
onChange?: (modelId: string, providerName: ProviderEnum) => void
|
||||
providerName: string
|
||||
onChange?: (modelId: string, providerName: string) => void
|
||||
readonly?: boolean
|
||||
}
|
||||
|
||||
|
|
@ -18,21 +19,17 @@ const ModelConfig: FC<IModelConfigProps> = ({
|
|||
readonly,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { agentThoughtModelList } = useProviderContext()
|
||||
|
||||
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>
|
||||
<ModelSelector
|
||||
popClassName="right-0"
|
||||
triggerIconSmall
|
||||
modelType={ModelType.textGeneration}
|
||||
supportAgentThought
|
||||
value={{
|
||||
modelName: modelId,
|
||||
providerName,
|
||||
}}
|
||||
onChange={(model) => {
|
||||
onChange?.(model.model_name, model.model_provider.provider_name)
|
||||
triggerClassName={`${readonly && '!cursor-not-allowed !opacity-60'}`}
|
||||
defaultModel={{ provider: providerName, model: modelId }}
|
||||
modelList={agentThoughtModelList}
|
||||
onSelect={(model) => {
|
||||
onChange?.(model.model, model.provider)
|
||||
}}
|
||||
readonly={readonly}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ 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 { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
|
||||
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_PLUGIN = {
|
||||
|
|
@ -76,8 +76,8 @@ const Main: FC<IMainProps> = () => {
|
|||
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,
|
||||
providerName: prevConfig?.providerName || agentThoughtModelList[0]?.provider,
|
||||
modelId: prevConfig?.modelId || agentThoughtModelList[0]?.models[0]?.model,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -452,9 +452,16 @@ const Main: FC<IMainProps> = () => {
|
|||
const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
|
||||
const [errorHappened, setErrorHappened] = useState(false)
|
||||
const [isResponsingConIsCurrCon, setIsResponsingConCurrCon, getIsResponsingConIsCurrCon] = useGetState(true)
|
||||
const initConfig = getInitConfig('model')
|
||||
const [modelId, setModeId] = useState<string>((initConfig as any)?.modelId as string)
|
||||
const [providerName, setProviderName] = useState<string>((initConfig as any)?.providerName)
|
||||
const { currentModel } = useCurrentProviderAndModel(
|
||||
agentThoughtModelList,
|
||||
{ provider: providerName, model: modelId },
|
||||
)
|
||||
const handleSend = async (message: string) => {
|
||||
if (isNewConversation) {
|
||||
const isModelSelected = modelId && !!agentThoughtModelList.find(item => item.model_name === modelId)
|
||||
const isModelSelected = modelId && !!currentModel
|
||||
if (!isModelSelected) {
|
||||
notify({ type: 'error', message: t('appDebug.errorMessage.notSelectModel') })
|
||||
return
|
||||
|
|
@ -601,7 +608,7 @@ const Main: FC<IMainProps> = () => {
|
|||
setChatList(newListWithAnswer)
|
||||
},
|
||||
onMessageEnd: (messageEnd) => {
|
||||
responseItem.citation = messageEnd.retriever_resources
|
||||
responseItem.citation = messageEnd.metadata?.retriever_resources
|
||||
|
||||
const newListWithAnswer = produce(
|
||||
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
|
||||
|
|
@ -691,9 +698,6 @@ const Main: FC<IMainProps> = () => {
|
|||
/>
|
||||
)
|
||||
}
|
||||
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>)
|
||||
|
|
@ -707,7 +711,7 @@ const Main: FC<IMainProps> = () => {
|
|||
const configSetDefaultValue = () => {
|
||||
const initConfig = getInitConfig('model')
|
||||
setModeId((initConfig as any)?.modelId as string)
|
||||
setProviderName((initConfig as any)?.providerName as ProviderEnum)
|
||||
setProviderName((initConfig as any)?.providerName)
|
||||
setPlugins(getInitConfig('plugin') as any)
|
||||
setDateSets([])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ const WorkplaceSelector = () => {
|
|||
const currentWorkspace = workspaces.find(v => v.current)
|
||||
|
||||
const handleSwitchWorkspace = async (tenant_id: string) => {
|
||||
console.log(tenant_id, currentWorkspace?.id)
|
||||
try {
|
||||
if (currentWorkspace?.id === tenant_id)
|
||||
return
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import LanguagePage from './language-page'
|
|||
import PluginPage from './plugin-page'
|
||||
import ApiBasedExtensionPage from './api-based-extension-page'
|
||||
import DataSourcePage from './data-source-page'
|
||||
import ModelPage from './model-page'
|
||||
import ModelProviderPage from './model-provider-page'
|
||||
import s from './index.module.css'
|
||||
import BillingPage from '@/app/components/billing/billing-page'
|
||||
import CustomPage from '@/app/components/custom/custom-page'
|
||||
|
|
@ -211,7 +211,7 @@ export default function AccountSetting({
|
|||
{activeMenu === 'billing' && <BillingPage />}
|
||||
{activeMenu === 'integrations' && <IntegrationsPage />}
|
||||
{activeMenu === 'language' && <LanguagePage />}
|
||||
{activeMenu === 'provider' && <ModelPage />}
|
||||
{activeMenu === 'provider' && <ModelProviderPage />}
|
||||
{activeMenu === 'data-source' && <DataSourcePage />}
|
||||
{activeMenu === 'plugin' && <PluginPage />}
|
||||
{activeMenu === 'api-based-extension' && <ApiBasedExtensionPage /> }
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export type ValidatedStatusState = {
|
|||
|
||||
export type Status = 'add' | 'fail' | 'success'
|
||||
|
||||
export type ValidateValue = Record<string, string | undefined | boolean>
|
||||
export type ValidateValue = Record<string, any>
|
||||
|
||||
export type ValidateCallback = {
|
||||
before: (v?: ValidateValue) => boolean | undefined
|
||||
|
|
|
|||
|
|
@ -1,80 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Anthropic, AnthropicText } from '@/app/components/base/icons/src/public/llm'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Anthropic',
|
||||
'zh-Hans': 'Anthropic',
|
||||
},
|
||||
icon: <Anthropic className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.anthropic,
|
||||
titleIcon: {
|
||||
'en': <AnthropicText className='h-5' />,
|
||||
'zh-Hans': <AnthropicText className='h-5' />,
|
||||
},
|
||||
subTitleIcon: <Anthropic className='h-6' />,
|
||||
desc: {
|
||||
'en': 'Anthropic’s powerful models, such as Claude 2 and Claude Instant.',
|
||||
'zh-Hans': 'Anthropic 的强大模型,例如 Claude 2 和 Claude Instant。',
|
||||
},
|
||||
bgColor: 'bg-[#F0F0EB]',
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.anthropic,
|
||||
title: {
|
||||
'en': 'Anthropic',
|
||||
'zh-Hans': 'Anthropic',
|
||||
},
|
||||
icon: <Anthropic className='h-6' />,
|
||||
link: {
|
||||
href: 'https://console.anthropic.com/account/keys',
|
||||
label: {
|
||||
'en': 'Get your API key from Anthropic',
|
||||
'zh-Hans': '从 Anthropic 获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['anthropic_api_key'],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'anthropic_api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API key here',
|
||||
'zh-Hans': '在此输入您的 API Key',
|
||||
},
|
||||
},
|
||||
...(
|
||||
IS_CE_EDITION
|
||||
? [{
|
||||
type: 'text',
|
||||
key: 'anthropic_api_url',
|
||||
required: false,
|
||||
label: {
|
||||
'en': 'Custom API Domain',
|
||||
'zh-Hans': '自定义 API 域名',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API domain, eg: https://example.com/xxx(Optional)',
|
||||
'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx(选填)',
|
||||
},
|
||||
help: {
|
||||
'en': 'Configurable custom Anthropic API server url.',
|
||||
'zh-Hans': '可配置自定义 Anthropic API 服务器地址。',
|
||||
},
|
||||
}]
|
||||
: []
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
export default config
|
||||
|
|
@ -1,189 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { AzureOpenaiService, AzureOpenaiServiceText, OpenaiBlue } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Azure OpenAI Service',
|
||||
'zh-Hans': 'Azure OpenAI Service',
|
||||
},
|
||||
icon: <OpenaiBlue className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.azure_openai,
|
||||
titleIcon: {
|
||||
'en': <AzureOpenaiServiceText className='h-6' />,
|
||||
'zh-Hans': <AzureOpenaiServiceText className='h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.azure_openai,
|
||||
title: {
|
||||
'en': 'Azure OpenAI Service Model',
|
||||
'zh-Hans': 'Azure OpenAI Service Model',
|
||||
},
|
||||
icon: <AzureOpenaiService className='h-6' />,
|
||||
link: {
|
||||
href: 'https://azure.microsoft.com/en-us/products/ai-services/openai-service',
|
||||
label: {
|
||||
'en': 'Get your API key from Azure',
|
||||
'zh-Hans': '从 Azure 获取 API Key',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
},
|
||||
validateKeys: [
|
||||
'model_name',
|
||||
'model_type',
|
||||
'openai_api_base',
|
||||
'openai_api_key',
|
||||
'base_model_name',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Deployment Name',
|
||||
'zh-Hans': '部署名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Deployment Name here, matching the Azure deployment name.',
|
||||
'zh-Hans': '在此输入您的部署名称,需要与 Azure 的部署名称匹配',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'openai_api_base',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Endpoint URL',
|
||||
'zh-Hans': 'API 域名',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API Endpoint, eg: https://example.com/xxx',
|
||||
'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'openai_api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API key here',
|
||||
'zh-Hans': '在此输入您的 API Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
key: 'base_model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Base Model',
|
||||
'zh-Hans': '基础模型',
|
||||
},
|
||||
options: (v) => {
|
||||
if (v.model_type === 'text-generation') {
|
||||
return [
|
||||
{
|
||||
key: 'gpt-35-turbo',
|
||||
label: {
|
||||
'en': 'gpt-35-turbo',
|
||||
'zh-Hans': 'gpt-35-turbo',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'gpt-35-turbo-16k',
|
||||
label: {
|
||||
'en': 'gpt-35-turbo-16k',
|
||||
'zh-Hans': 'gpt-35-turbo-16k',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'gpt-4',
|
||||
label: {
|
||||
'en': 'gpt-4',
|
||||
'zh-Hans': 'gpt-4',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'gpt-4-32k',
|
||||
label: {
|
||||
'en': 'gpt-4-32k',
|
||||
'zh-Hans': 'gpt-4-32k',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'gpt-4-1106-preview',
|
||||
label: {
|
||||
'en': 'gpt-4-1106-preview',
|
||||
'zh-Hans': 'gpt-4-1106-preview',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'gpt-4-vision-preview',
|
||||
label: {
|
||||
'en': 'gpt-4-vision-preview',
|
||||
'zh-Hans': 'gpt-4-vision-preview',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'text-davinci-003',
|
||||
label: {
|
||||
'en': 'text-davinci-003',
|
||||
'zh-Hans': 'text-davinci-003',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
if (v.model_type === 'embeddings') {
|
||||
return [
|
||||
{
|
||||
key: 'text-embedding-ada-002',
|
||||
label: {
|
||||
'en': 'text-embedding-ada-002',
|
||||
'zh-Hans': 'text-embedding-ada-002',
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
return []
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { BaichuanTextCn } from '@/app/components/base/icons/src/image/llm'
|
||||
import {
|
||||
Baichuan,
|
||||
BaichuanText,
|
||||
} from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'BAICHUAN AI',
|
||||
'zh-Hans': '百川智能',
|
||||
},
|
||||
icon: <Baichuan className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.baichuan,
|
||||
titleIcon: {
|
||||
'en': <BaichuanText className='w-[124px] h-6' />,
|
||||
'zh-Hans': <BaichuanTextCn className='w-[100px] h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.baichuan,
|
||||
title: {
|
||||
'en': 'BAICHUAN AI',
|
||||
'zh-Hans': '百川智能',
|
||||
},
|
||||
icon: <Baichuan className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://platform.baichuan-ai.com/console/apikey',
|
||||
label: {
|
||||
'en': 'Get your API key from BAICHUAN AI',
|
||||
'zh-Hans': '从百川智能获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['api_key', 'secret_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',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'secret_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Secret Key',
|
||||
'zh-Hans': 'Secret Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Secret key here',
|
||||
'zh-Hans': '在此输入您的 Secret Key',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Chatglm, ChatglmText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'ChatGLM',
|
||||
'zh-Hans': 'ChatGLM',
|
||||
},
|
||||
icon: <Chatglm className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.chatglm,
|
||||
titleIcon: {
|
||||
'en': <ChatglmText className='h-6' />,
|
||||
'zh-Hans': <ChatglmText className='h-6' />,
|
||||
},
|
||||
disable: {
|
||||
tip: {
|
||||
'en': 'Only supports the ',
|
||||
'zh-Hans': '仅支持',
|
||||
},
|
||||
link: {
|
||||
href: {
|
||||
'en': 'https://docs.dify.ai/getting-started/install-self-hosted',
|
||||
'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted',
|
||||
},
|
||||
label: {
|
||||
'en': 'community open-source version',
|
||||
'zh-Hans': '社区开源版本',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.chatglm,
|
||||
title: {
|
||||
'en': 'ChatGLM',
|
||||
'zh-Hans': 'ChatGLM',
|
||||
},
|
||||
icon: <Chatglm className='h-6' />,
|
||||
link: {
|
||||
href: 'https://github.com/THUDM/ChatGLM-6B#api%E9%83%A8%E7%BD%B2',
|
||||
label: {
|
||||
'en': 'How to deploy ChatGLM',
|
||||
'zh-Hans': '如何部署 ChatGLM',
|
||||
},
|
||||
},
|
||||
validateKeys: ['api_base'],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'api_base',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Custom API Domain',
|
||||
'zh-Hans': '自定义 API 域名',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API domain, eg: https://example.com/xxx',
|
||||
'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
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' />,
|
||||
},
|
||||
hit: {
|
||||
'en': 'Rerank Model Supported',
|
||||
'zh-Hans': '支持 Rerank 模型',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.cohere,
|
||||
title: {
|
||||
'en': 'Rerank Model',
|
||||
'zh-Hans': 'Rerank 模型',
|
||||
},
|
||||
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
|
||||
|
|
@ -1,278 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { FormValue, ProviderConfig } from '../declarations'
|
||||
import { Huggingface, HuggingfaceText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Hugging Face',
|
||||
'zh-Hans': 'Hugging Face',
|
||||
},
|
||||
icon: <Huggingface className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.huggingface_hub,
|
||||
titleIcon: {
|
||||
'en': <HuggingfaceText className='h-6' />,
|
||||
'zh-Hans': <HuggingfaceText className='h-6' />,
|
||||
},
|
||||
hit: {
|
||||
'en': '🐑 Llama 2 Supported',
|
||||
'zh-Hans': '🐑 Llama 2 已支持',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.huggingface_hub,
|
||||
title: {
|
||||
'en': 'Hugging Face Model',
|
||||
'zh-Hans': 'Hugging Face Model',
|
||||
},
|
||||
icon: <Huggingface className='h-6' />,
|
||||
link: {
|
||||
href: 'https://huggingface.co/settings/tokens',
|
||||
label: {
|
||||
'en': 'Get your API key from Hugging Face Hub',
|
||||
'zh-Hans': '从 Hugging Face Hub 获取 API Key',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
huggingfacehub_api_type: 'hosted_inference_api',
|
||||
task_type: 'text-generation',
|
||||
},
|
||||
validateKeys: (v?: FormValue) => {
|
||||
if (v?.huggingfacehub_api_type === 'hosted_inference_api') {
|
||||
return [
|
||||
'huggingfacehub_api_token',
|
||||
'model_name',
|
||||
]
|
||||
}
|
||||
if (v?.huggingfacehub_api_type === 'inference_endpoints') {
|
||||
if (v.model_type === 'embeddings') {
|
||||
return [
|
||||
'huggingfacehub_api_token',
|
||||
'huggingface_namespace',
|
||||
'model_name',
|
||||
'huggingfacehub_endpoint_url',
|
||||
'task_type',
|
||||
]
|
||||
}
|
||||
return [
|
||||
'huggingfacehub_api_token',
|
||||
'model_name',
|
||||
'huggingfacehub_endpoint_url',
|
||||
'task_type',
|
||||
]
|
||||
}
|
||||
return []
|
||||
},
|
||||
filterValue: (v?: FormValue) => {
|
||||
let filteredKeys: string[] = []
|
||||
if (v?.huggingfacehub_api_type === 'hosted_inference_api') {
|
||||
filteredKeys = [
|
||||
'huggingfacehub_api_type',
|
||||
'huggingfacehub_api_token',
|
||||
'model_name',
|
||||
'model_type',
|
||||
]
|
||||
}
|
||||
if (v?.huggingfacehub_api_type === 'inference_endpoints') {
|
||||
if (v.model_type === 'embeddings') {
|
||||
filteredKeys = [
|
||||
'huggingfacehub_api_type',
|
||||
'huggingfacehub_api_token',
|
||||
'huggingface_namespace',
|
||||
'model_name',
|
||||
'huggingfacehub_endpoint_url',
|
||||
'task_type',
|
||||
'model_type',
|
||||
]
|
||||
}
|
||||
else {
|
||||
filteredKeys = [
|
||||
'huggingfacehub_api_type',
|
||||
'huggingfacehub_api_token',
|
||||
'model_name',
|
||||
'huggingfacehub_endpoint_url',
|
||||
'task_type',
|
||||
'model_type',
|
||||
]
|
||||
}
|
||||
}
|
||||
return filteredKeys.reduce((prev: FormValue, next: string) => {
|
||||
prev[next] = v?.[next] || ''
|
||||
return prev
|
||||
}, {})
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'chat',
|
||||
// label: {
|
||||
// 'en': 'Chat',
|
||||
// 'zh-Hans': '聊天',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'huggingfacehub_api_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Endpoint Type',
|
||||
'zh-Hans': '端点类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'hosted_inference_api',
|
||||
label: {
|
||||
'en': 'Hosted Inference API',
|
||||
'zh-Hans': 'Hosted Inference API',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'inference_endpoints',
|
||||
label: {
|
||||
'en': 'Inference Endpoints',
|
||||
'zh-Hans': 'Inference Endpoints',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'huggingfacehub_api_token',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Token',
|
||||
'zh-Hans': 'API Token',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Hugging Face Hub API Token here',
|
||||
'zh-Hans': '在此输入您的 Hugging Face Hub API Token',
|
||||
},
|
||||
},
|
||||
{
|
||||
hidden: (value?: FormValue) => !(value?.huggingfacehub_api_type === 'inference_endpoints' && value?.model_type === 'embeddings'),
|
||||
type: 'text',
|
||||
key: 'huggingface_namespace',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'User Name / Organization Name',
|
||||
'zh-Hans': '用户名 / 组织名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your User Name / Organization Name here',
|
||||
'zh-Hans': '在此输入您的用户名 / 组织名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Name',
|
||||
'zh-Hans': '模型名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Name here',
|
||||
'zh-Hans': '在此输入您的模型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
hidden: (value?: FormValue) => value?.huggingfacehub_api_type === 'hosted_inference_api',
|
||||
type: 'text',
|
||||
key: 'huggingfacehub_endpoint_url',
|
||||
label: {
|
||||
'en': 'Endpoint URL',
|
||||
'zh-Hans': '端点 URL',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Endpoint URL here',
|
||||
'zh-Hans': '在此输入您的端点 URL',
|
||||
},
|
||||
},
|
||||
{
|
||||
hidden: (value?: FormValue) => value?.huggingfacehub_api_type === 'hosted_inference_api' || value?.model_type === 'embeddings',
|
||||
type: 'radio',
|
||||
key: 'task_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Task',
|
||||
'zh-Hans': 'Task',
|
||||
},
|
||||
options: (value?: FormValue) => {
|
||||
if (value?.model_type === 'chat') {
|
||||
return [{
|
||||
key: 'question-answer',
|
||||
label: {
|
||||
'en': '问答',
|
||||
'zh-Hans': 'Question Answer',
|
||||
},
|
||||
}]
|
||||
}
|
||||
return [
|
||||
{
|
||||
key: 'text2text-generation',
|
||||
label: {
|
||||
'en': 'Text-to-Text Generation',
|
||||
'zh-Hans': 'Text-to-Text Generation',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': 'Text Generation',
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
{
|
||||
hidden: (value?: FormValue) => !(value?.huggingfacehub_api_type === 'inference_endpoints' && value?.model_type === 'embeddings'),
|
||||
type: 'radio',
|
||||
key: 'task_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Task',
|
||||
'zh-Hans': 'Task',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'feature-extraction',
|
||||
label: {
|
||||
'en': 'Feature Extraction',
|
||||
'zh-Hans': 'Feature Extraction',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
import openai from './openai'
|
||||
import anthropic from './anthropic'
|
||||
import azure_openai from './azure_openai'
|
||||
import replicate from './replicate'
|
||||
import huggingface_hub from './huggingface_hub'
|
||||
import wenxin from './wenxin'
|
||||
import tongyi from './tongyi'
|
||||
import spark from './spark'
|
||||
import minimax from './minimax'
|
||||
import chatglm from './chatglm'
|
||||
import xinference from './xinference'
|
||||
import openllm from './openllm'
|
||||
import localai from './localai'
|
||||
import zhipuai from './zhipuai'
|
||||
import baichuan from './baichuan'
|
||||
import cohere from './cohere'
|
||||
import jina from './jina'
|
||||
|
||||
export default {
|
||||
openai,
|
||||
anthropic,
|
||||
azure_openai,
|
||||
replicate,
|
||||
huggingface_hub,
|
||||
wenxin,
|
||||
tongyi,
|
||||
spark,
|
||||
minimax,
|
||||
chatglm,
|
||||
xinference,
|
||||
openllm,
|
||||
localai,
|
||||
zhipuai,
|
||||
baichuan,
|
||||
cohere,
|
||||
jina,
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Jina, JinaText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Jina AI',
|
||||
'zh-Hans': 'Jina AI',
|
||||
},
|
||||
icon: <Jina className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.jina,
|
||||
titleIcon: {
|
||||
'en': <JinaText className='w-[58px] h-6' />,
|
||||
'zh-Hans': <JinaText className='w-[58px] h-6' />,
|
||||
},
|
||||
hit: {
|
||||
'en': 'Embedding Model Supported',
|
||||
'zh-Hans': '支持 Embedding 模型',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.jina,
|
||||
title: {
|
||||
'en': 'Embedding Model',
|
||||
'zh-Hans': 'Embedding 模型',
|
||||
},
|
||||
icon: <JinaText className='w-[58px] h-6' />,
|
||||
link: {
|
||||
href: 'https://jina.ai/embeddings/',
|
||||
label: {
|
||||
'en': 'Get your API key from Jina AI',
|
||||
'zh-Hans': '从 Jina AI 获取 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
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { FormValue, ProviderConfig } from '../declarations'
|
||||
import { Localai, LocalaiText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'LocalAI',
|
||||
'zh-Hans': 'LocalAI',
|
||||
},
|
||||
icon: <Localai className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.localai,
|
||||
titleIcon: {
|
||||
'en': <LocalaiText className='h-6' />,
|
||||
'zh-Hans': <LocalaiText className='h-6' />,
|
||||
},
|
||||
disable: {
|
||||
tip: {
|
||||
'en': 'Only supports the ',
|
||||
'zh-Hans': '仅支持',
|
||||
},
|
||||
link: {
|
||||
href: {
|
||||
'en': 'https://docs.dify.ai/getting-started/install-self-hosted',
|
||||
'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted',
|
||||
},
|
||||
label: {
|
||||
'en': 'community open-source version',
|
||||
'zh-Hans': '社区开源版本',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.localai,
|
||||
title: {
|
||||
'en': 'LocalAI',
|
||||
'zh-Hans': 'LocalAI',
|
||||
},
|
||||
icon: <Localai className='h-6' />,
|
||||
link: {
|
||||
href: 'https://github.com/go-skynet/LocalAI',
|
||||
label: {
|
||||
'en': 'How to deploy LocalAI',
|
||||
'zh-Hans': '如何部署 LocalAI',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
completion_type: 'completion',
|
||||
},
|
||||
validateKeys: (v?: FormValue) => {
|
||||
if (v?.model_type === 'text-generation') {
|
||||
return [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
'completion_type',
|
||||
]
|
||||
}
|
||||
if (v?.model_type === 'embeddings') {
|
||||
return [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
]
|
||||
}
|
||||
return []
|
||||
},
|
||||
filterValue: (v?: FormValue) => {
|
||||
let filteredKeys: string[] = []
|
||||
if (v?.model_type === 'text-generation') {
|
||||
filteredKeys = [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
'completion_type',
|
||||
]
|
||||
}
|
||||
if (v?.model_type === 'embeddings') {
|
||||
filteredKeys = [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
]
|
||||
}
|
||||
return filteredKeys.reduce((prev: FormValue, next: string) => {
|
||||
prev[next] = v?.[next] || ''
|
||||
return prev
|
||||
}, {})
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Name',
|
||||
'zh-Hans': '模型名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Name here',
|
||||
'zh-Hans': '在此输入您的模型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
hidden: (value?: FormValue) => value?.model_type === 'embeddings',
|
||||
type: 'radio',
|
||||
key: 'completion_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Completion Type',
|
||||
'zh-Hans': 'Completion Type',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'completion',
|
||||
label: {
|
||||
'en': 'Completion',
|
||||
'zh-Hans': 'Completion',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'chat_completion',
|
||||
label: {
|
||||
'en': 'Chat Completion',
|
||||
'zh-Hans': 'Chat Completion',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'server_url',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Server url',
|
||||
'zh-Hans': 'Server url',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Server Url, eg: https://example.com/xxx',
|
||||
'zh-Hans': '在此输入您的 Server Url,如:https://example.com/xxx',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Minimax, MinimaxText } from '@/app/components/base/icons/src/image/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'MINIMAX',
|
||||
'zh-Hans': 'MINIMAX',
|
||||
},
|
||||
icon: <Minimax className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.minimax,
|
||||
titleIcon: {
|
||||
'en': <MinimaxText className='w-[84px] h-6' />,
|
||||
'zh-Hans': <MinimaxText className='w-[84px] h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.minimax,
|
||||
title: {
|
||||
'en': 'MiniMax',
|
||||
'zh-Hans': 'MiniMax',
|
||||
},
|
||||
icon: <Minimax className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://api.minimax.chat/user-center/basic-information/interface-key',
|
||||
label: {
|
||||
'en': 'Get your API key from MiniMax',
|
||||
'zh-Hans': '从 MiniMax 获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: [
|
||||
'minimax_api_key',
|
||||
'minimax_group_id',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'minimax_api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API key here',
|
||||
'zh-Hans': '在此输入您的 API Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'minimax_group_id',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Group ID',
|
||||
'zh-Hans': 'Group ID',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Group ID here',
|
||||
'zh-Hans': '在此输入您的 Group ID',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { OpenaiBlack, OpenaiText, OpenaiTransparent } from '@/app/components/base/icons/src/public/llm'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'OpenAI',
|
||||
'zh-Hans': 'OpenAI',
|
||||
},
|
||||
icon: <OpenaiBlack className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.openai,
|
||||
titleIcon: {
|
||||
'en': <OpenaiText className='h-5' />,
|
||||
'zh-Hans': <OpenaiText className='h-5' />,
|
||||
},
|
||||
subTitleIcon: <OpenaiBlack className='w-6 h-6' />,
|
||||
desc: {
|
||||
'en': 'Models provided by OpenAI, such as GPT-3.5-Turbo and GPT-4.',
|
||||
'zh-Hans': 'OpenAI 提供的模型,例如 GPT-3.5-Turbo 和 GPT-4。',
|
||||
},
|
||||
bgColor: 'bg-gray-200',
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.openai,
|
||||
title: {
|
||||
'en': 'OpenAI',
|
||||
'zh-Hans': 'OpenAI',
|
||||
},
|
||||
icon: <OpenaiTransparent className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://platform.openai.com/account/api-keys',
|
||||
label: {
|
||||
'en': 'Get your API key from OpenAI',
|
||||
'zh-Hans': '从 OpenAI 获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['openai_api_key'],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'openai_api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API key here',
|
||||
'zh-Hans': '在此输入您的 API Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'openai_organization',
|
||||
required: false,
|
||||
label: {
|
||||
'en': 'Organization ID',
|
||||
'zh-Hans': '组织 ID',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Organization ID(Optional)',
|
||||
'zh-Hans': '在此输入您的组织 ID(选填)',
|
||||
},
|
||||
},
|
||||
...(
|
||||
IS_CE_EDITION
|
||||
? [{
|
||||
type: 'text',
|
||||
key: 'openai_api_base',
|
||||
required: false,
|
||||
label: {
|
||||
'en': 'Custom API Domain',
|
||||
'zh-Hans': '自定义 API 域名',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your API domain, eg: https://example.com/xxx(Optional)',
|
||||
'zh-Hans': '在此输入您的 API 域名,如:https://example.com/xxx(选填)',
|
||||
},
|
||||
help: {
|
||||
'en': 'You can configure your server compatible with the OpenAI API specification, or proxy mirror address',
|
||||
'zh-Hans': '可配置您的兼容 OpenAI API 规范的服务器,或者代理镜像地址',
|
||||
},
|
||||
}]
|
||||
: []
|
||||
),
|
||||
],
|
||||
},
|
||||
}
|
||||
export default config
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Openllm, OpenllmText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'OpenLLM',
|
||||
'zh-Hans': 'OpenLLM',
|
||||
},
|
||||
icon: <Openllm className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.openllm,
|
||||
titleIcon: {
|
||||
'en': <OpenllmText className='h-6' />,
|
||||
'zh-Hans': <OpenllmText className='h-6' />,
|
||||
},
|
||||
disable: {
|
||||
tip: {
|
||||
'en': 'Only supports the ',
|
||||
'zh-Hans': '仅支持',
|
||||
},
|
||||
link: {
|
||||
href: {
|
||||
'en': 'https://docs.dify.ai/getting-started/install-self-hosted',
|
||||
'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted',
|
||||
},
|
||||
label: {
|
||||
'en': 'community open-source version',
|
||||
'zh-Hans': '社区开源版本',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.openllm,
|
||||
title: {
|
||||
'en': 'OpenLLM',
|
||||
'zh-Hans': 'OpenLLM',
|
||||
},
|
||||
icon: <Openllm className='h-6' />,
|
||||
link: {
|
||||
href: 'https://github.com/bentoml/OpenLLM',
|
||||
label: {
|
||||
'en': 'How to deploy OpenLLM',
|
||||
'zh-Hans': '如何部署 OpenLLM',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
},
|
||||
validateKeys: [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Name',
|
||||
'zh-Hans': '模型名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Name here',
|
||||
'zh-Hans': '在此输入您的模型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'server_url',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Server url',
|
||||
'zh-Hans': 'Server url',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Server Url, eg: https://example.com/xxx',
|
||||
'zh-Hans': '在此输入您的 Server Url,如:https://example.com/xxx',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Replicate, ReplicateText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Replicate',
|
||||
'zh-Hans': 'Replicate',
|
||||
},
|
||||
icon: <Replicate className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.replicate,
|
||||
titleIcon: {
|
||||
'en': <ReplicateText className='h-6' />,
|
||||
'zh-Hans': <ReplicateText className='h-6' />,
|
||||
},
|
||||
hit: {
|
||||
'en': '🐑 Llama 2 Supported',
|
||||
'zh-Hans': '🐑 Llama 2 已支持',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.replicate,
|
||||
title: {
|
||||
'en': 'Replicate Model',
|
||||
'zh-Hans': 'Replicate Model',
|
||||
},
|
||||
icon: <Replicate className='h-6' />,
|
||||
link: {
|
||||
href: 'https://replicate.com/account/api-tokens',
|
||||
label: {
|
||||
'en': 'Get your API key from Replicate',
|
||||
'zh-Hans': '从 Replicate 获取 API Key',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
},
|
||||
validateKeys: [
|
||||
'model_type',
|
||||
'replicate_api_token',
|
||||
'model_name',
|
||||
'model_version',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
// {
|
||||
// key: 'chat',
|
||||
// label: {
|
||||
// 'en': 'Chat',
|
||||
// 'zh-Hans': '聊天',
|
||||
// },
|
||||
// },
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'replicate_api_token',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'API Key',
|
||||
'zh-Hans': 'API Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Replicate API key here',
|
||||
'zh-Hans': '在此输入您的 Replicate API Key',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Name',
|
||||
'zh-Hans': '模型名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Name here',
|
||||
'zh-Hans': '在此输入您的模型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_version',
|
||||
label: {
|
||||
'en': 'Model Version',
|
||||
'zh-Hans': '模型版本',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Version here',
|
||||
'zh-Hans': '在此输入您的模型版本',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { IflytekSpark, IflytekSparkText, IflytekSparkTextCn } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'iFLYTEK SPARK',
|
||||
'zh-Hans': '讯飞星火',
|
||||
},
|
||||
icon: <IflytekSpark className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.spark,
|
||||
titleIcon: {
|
||||
'en': <IflytekSparkText className='h-6' />,
|
||||
'zh-Hans': <IflytekSparkTextCn className='h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.spark,
|
||||
title: {
|
||||
'en': 'iFLYTEK SPARK',
|
||||
'zh-Hans': '讯飞星火',
|
||||
},
|
||||
icon: <IflytekSpark className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://www.xfyun.cn/solutions/xinghuoAPI',
|
||||
label: {
|
||||
'en': 'Get your API key from iFLYTEK SPARK',
|
||||
'zh-Hans': '从讯飞星火获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: [
|
||||
'app_id',
|
||||
'api_key',
|
||||
'api_secret',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'app_id',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'APPID',
|
||||
'zh-Hans': 'APPID',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your APPID here',
|
||||
'zh-Hans': '在此输入您的 APPID',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'api_secret',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'APISecret',
|
||||
'zh-Hans': 'APISecret',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your APISecret here',
|
||||
'zh-Hans': '在此输入您的 APISecret',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'APIKey',
|
||||
'zh-Hans': 'APIKey',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your APIKey here',
|
||||
'zh-Hans': '在此输入您的 APIKey',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Tongyi, TongyiText, TongyiTextCn } from '@/app/components/base/icons/src/image/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'TONGYI QIANWEN',
|
||||
'zh-Hans': '通义千问',
|
||||
},
|
||||
icon: <Tongyi className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.tongyi,
|
||||
titleIcon: {
|
||||
'en': <TongyiText className='w-[88px] h-6' />,
|
||||
'zh-Hans': <TongyiTextCn className='w-[100px] h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.tongyi,
|
||||
title: {
|
||||
'en': 'Tongyi',
|
||||
'zh-Hans': '通义千问',
|
||||
},
|
||||
icon: <Tongyi className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://dashscope.console.aliyun.com/api-key_management',
|
||||
label: {
|
||||
'en': 'Get your API key from AliCloud',
|
||||
'zh-Hans': '从阿里云获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['dashscope_api_key'],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'dashscope_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
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Wxyy, WxyyText, WxyyTextCn } from '@/app/components/base/icons/src/image/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'WENXIN YIYAN',
|
||||
'zh-Hans': '文心一言',
|
||||
},
|
||||
icon: <Wxyy className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.wenxin,
|
||||
titleIcon: {
|
||||
'en': <WxyyText className='w-[124px] h-6' />,
|
||||
'zh-Hans': <WxyyTextCn className='w-[100px] h-6' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.wenxin,
|
||||
title: {
|
||||
'en': 'WENXINYIYAN',
|
||||
'zh-Hans': '文心一言',
|
||||
},
|
||||
icon: <Wxyy className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application',
|
||||
label: {
|
||||
'en': 'Get your API key from Baidu',
|
||||
'zh-Hans': '从百度获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: ['api_key', 'secret_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',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'secret_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Secret Key',
|
||||
'zh-Hans': 'Secret Key',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Secret key here',
|
||||
'zh-Hans': '在此输入您的 Secret Key',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { XorbitsInference, XorbitsInferenceText } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'Xinference',
|
||||
'zh-Hans': 'Xinference',
|
||||
},
|
||||
icon: <XorbitsInference className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.xinference,
|
||||
titleIcon: {
|
||||
'en': <XorbitsInferenceText className='h-6' />,
|
||||
'zh-Hans': <XorbitsInferenceText className='h-6' />,
|
||||
},
|
||||
disable: {
|
||||
tip: {
|
||||
'en': 'Only supports the ',
|
||||
'zh-Hans': '仅支持',
|
||||
},
|
||||
link: {
|
||||
href: {
|
||||
'en': 'https://docs.dify.ai/getting-started/install-self-hosted',
|
||||
'zh-Hans': 'https://docs.dify.ai/v/zh-hans/getting-started/install-self-hosted',
|
||||
},
|
||||
label: {
|
||||
'en': 'community open-source version',
|
||||
'zh-Hans': '社区开源版本',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.xinference,
|
||||
title: {
|
||||
'en': 'Xinference',
|
||||
'zh-Hans': 'Xinference',
|
||||
},
|
||||
icon: <XorbitsInference className='h-6' />,
|
||||
link: {
|
||||
href: 'https://github.com/xorbitsai/inference',
|
||||
label: {
|
||||
'en': 'How to deploy Xinference',
|
||||
'zh-Hans': '如何部署 Xinference',
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
model_type: 'text-generation',
|
||||
},
|
||||
validateKeys: [
|
||||
'model_type',
|
||||
'model_name',
|
||||
'server_url',
|
||||
'model_uid',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'radio',
|
||||
key: 'model_type',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Type',
|
||||
'zh-Hans': '模型类型',
|
||||
},
|
||||
options: [
|
||||
{
|
||||
key: 'text-generation',
|
||||
label: {
|
||||
'en': 'Text Generation',
|
||||
'zh-Hans': '文本生成',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'embeddings',
|
||||
label: {
|
||||
'en': 'Embeddings',
|
||||
'zh-Hans': 'Embeddings',
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'reranking',
|
||||
label: {
|
||||
'en': 'Rerank',
|
||||
'zh-Hans': 'Rerank',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_name',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model Name',
|
||||
'zh-Hans': '模型名称',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model Name here',
|
||||
'zh-Hans': '在此输入您的模型名称',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'server_url',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Server Url',
|
||||
'zh-Hans': 'Server Url',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Server url, eg: https://example.com/xxx',
|
||||
'zh-Hans': '在此输入您的 Server url,如:https://example.com/xxx',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
key: 'model_uid',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'Model UID',
|
||||
'zh-Hans': 'Model UID',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your Model UID',
|
||||
'zh-Hans': '在此输入您的 Model UID',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
import { ProviderEnum } from '../declarations'
|
||||
import type { ProviderConfig } from '../declarations'
|
||||
import { Zhipuai, ZhipuaiText, ZhipuaiTextCn } from '@/app/components/base/icons/src/public/llm'
|
||||
|
||||
const config: ProviderConfig = {
|
||||
selector: {
|
||||
name: {
|
||||
'en': 'ZHIPU AI',
|
||||
'zh-Hans': '智谱 AI',
|
||||
},
|
||||
icon: <Zhipuai className='w-full h-full' />,
|
||||
},
|
||||
item: {
|
||||
key: ProviderEnum.zhipuai,
|
||||
titleIcon: {
|
||||
'en': <ZhipuaiText className='-ml-1 h-7' />,
|
||||
'zh-Hans': <ZhipuaiTextCn className='h-8' />,
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
key: ProviderEnum.zhipuai,
|
||||
title: {
|
||||
'en': 'ZHIPU AI',
|
||||
'zh-Hans': '智谱 AI',
|
||||
},
|
||||
icon: <Zhipuai className='w-6 h-6' />,
|
||||
link: {
|
||||
href: 'https://open.bigmodel.cn/usercenter/apikeys',
|
||||
label: {
|
||||
'en': 'Get your API key from ZHIPU AI',
|
||||
'zh-Hans': '从智谱 AI 获取 API Key',
|
||||
},
|
||||
},
|
||||
validateKeys: [
|
||||
'api_key',
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
type: 'text',
|
||||
key: 'api_key',
|
||||
required: true,
|
||||
label: {
|
||||
'en': 'APIKey',
|
||||
'zh-Hans': 'APIKey',
|
||||
},
|
||||
placeholder: {
|
||||
'en': 'Enter your APIKey here',
|
||||
'zh-Hans': '在此输入您的 APIKey',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default config
|
||||
|
|
@ -1,163 +0,0 @@
|
|||
import type { ReactElement } from 'react'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
|
||||
export type FormValue = Record<string, string>
|
||||
|
||||
export type TypeWithI18N<T = string> = {
|
||||
'en': T
|
||||
'zh-Hans': T
|
||||
}
|
||||
|
||||
export type Option = {
|
||||
key: string
|
||||
label: TypeWithI18N
|
||||
}
|
||||
|
||||
export type ProviderSelector = {
|
||||
name: TypeWithI18N
|
||||
icon: ReactElement
|
||||
}
|
||||
|
||||
export type Field = {
|
||||
hidden?: (v?: FormValue) => boolean
|
||||
type: string
|
||||
key: string
|
||||
required?: boolean
|
||||
label: TypeWithI18N
|
||||
options?: Option[] | ((v: FormValue) => Option[])
|
||||
placeholder?: TypeWithI18N
|
||||
help?: TypeWithI18N
|
||||
}
|
||||
|
||||
export enum ProviderEnum {
|
||||
'openai' = 'openai',
|
||||
'anthropic' = 'anthropic',
|
||||
'replicate' = 'replicate',
|
||||
'azure_openai' = 'azure_openai',
|
||||
'huggingface_hub' = 'huggingface_hub',
|
||||
'tongyi' = 'tongyi',
|
||||
'wenxin' = 'wenxin',
|
||||
'spark' = 'spark',
|
||||
'minimax' = 'minimax',
|
||||
'chatglm' = 'chatglm',
|
||||
'xinference' = 'xinference',
|
||||
'openllm' = 'openllm',
|
||||
'localai' = 'localai',
|
||||
'zhipuai' = 'zhipuai',
|
||||
'baichuan' = 'baichuan',
|
||||
'cohere' = 'cohere',
|
||||
'jina' = 'jina',
|
||||
}
|
||||
|
||||
export type ProviderConfigItem = {
|
||||
key: ProviderEnum
|
||||
titleIcon: TypeWithI18N<ReactElement>
|
||||
subTitleIcon?: ReactElement
|
||||
desc?: TypeWithI18N
|
||||
bgColor?: string
|
||||
hit?: TypeWithI18N
|
||||
disable?: {
|
||||
tip: TypeWithI18N
|
||||
link: {
|
||||
href: TypeWithI18N
|
||||
label: TypeWithI18N
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export enum ModelType {
|
||||
textGeneration = 'text-generation',
|
||||
embeddings = 'embeddings',
|
||||
speech2text = 'speech2text',
|
||||
reranking = 'reranking',
|
||||
}
|
||||
|
||||
export enum ModelFeature {
|
||||
agentThought = 'agent_thought',
|
||||
vision = 'vision',
|
||||
}
|
||||
|
||||
// backend defined model struct: /console/api/workspaces/current/models/model-type/:model_type
|
||||
export type BackendModel = {
|
||||
model_name: string
|
||||
model_display_name: string // not always exist
|
||||
model_type: ModelType
|
||||
model_mode: ModelModeType
|
||||
model_provider: {
|
||||
provider_name: ProviderEnum
|
||||
provider_type: PreferredProviderTypeEnum
|
||||
quota_type: 'trial' | 'paid'
|
||||
quota_unit: 'times' | 'tokens'
|
||||
quota_used: number
|
||||
quota_limit: number
|
||||
}
|
||||
features: ModelFeature[]
|
||||
}
|
||||
|
||||
export type ProviderConfigModal = {
|
||||
key: ProviderEnum
|
||||
title: TypeWithI18N
|
||||
icon: ReactElement
|
||||
defaultValue?: FormValue
|
||||
validateKeys?: string[] | ((v?: FormValue) => string[])
|
||||
filterValue?: (v?: FormValue) => FormValue
|
||||
fields: Field[]
|
||||
link: {
|
||||
href: string
|
||||
label: TypeWithI18N
|
||||
}
|
||||
}
|
||||
|
||||
export type ProviderConfig = {
|
||||
selector: ProviderSelector
|
||||
item: ProviderConfigItem
|
||||
modal: ProviderConfigModal
|
||||
}
|
||||
|
||||
export enum PreferredProviderTypeEnum {
|
||||
'system' = 'system',
|
||||
'custom' = 'custom',
|
||||
}
|
||||
export enum ModelFlexibilityEnum {
|
||||
'fixed' = 'fixed',
|
||||
'configurable' = 'configurable',
|
||||
}
|
||||
|
||||
export type ProviderCommon = {
|
||||
provider_name: ProviderEnum
|
||||
provider_type: PreferredProviderTypeEnum
|
||||
is_valid: boolean
|
||||
last_used: number
|
||||
}
|
||||
|
||||
export type ProviderWithQuota = {
|
||||
quota_type: string
|
||||
quota_unit: string
|
||||
quota_limit: number
|
||||
quota_used: number
|
||||
} & ProviderCommon
|
||||
|
||||
export type ProviderWithConfig = {
|
||||
config: Record<string, string>
|
||||
} & ProviderCommon
|
||||
|
||||
export type Model = {
|
||||
model_name: string
|
||||
model_type: string
|
||||
config: Record<string, string>
|
||||
}
|
||||
|
||||
export type ProviderWithModels = {
|
||||
models: Model[]
|
||||
} & ProviderCommon
|
||||
|
||||
export type ProviderInstance = ProviderWithQuota | ProviderWithConfig | ProviderWithModels
|
||||
|
||||
export type Provider = {
|
||||
preferred_provider_type: PreferredProviderTypeEnum
|
||||
model_flexibility: ModelFlexibilityEnum
|
||||
providers: ProviderInstance[]
|
||||
}
|
||||
export type ProviderMap = {
|
||||
[k in ProviderEnum]: Provider
|
||||
}
|
||||
|
|
@ -1,267 +0,0 @@
|
|||
import { useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type {
|
||||
FormValue,
|
||||
ProviderConfigModal,
|
||||
ProviderEnum,
|
||||
} from './declarations'
|
||||
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 {
|
||||
changeModelProviderPriority,
|
||||
deleteModelProvider,
|
||||
deleteModelProviderModel,
|
||||
fetchModelProviders,
|
||||
setModelProvider,
|
||||
} 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 I18n from '@/context/i18n'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
|
||||
const MODEL_CARD_LIST = [
|
||||
config.openai,
|
||||
config.anthropic,
|
||||
]
|
||||
|
||||
type DeleteModel = {
|
||||
model_name: string
|
||||
model_type: string
|
||||
}
|
||||
|
||||
const ModelPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const {
|
||||
updateModelList,
|
||||
textGenerationDefaultModel,
|
||||
embeddingsDefaultModel,
|
||||
speech2textDefaultModel,
|
||||
rerankDefaultModel,
|
||||
} = useProviderContext()
|
||||
const { data: providers, mutate: mutateProviders } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const { notify } = useToastContext()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [modelModalConfig, setModelModalConfig] = useState<ProviderConfigModal | undefined>(undefined)
|
||||
const [confirmShow, setConfirmShow] = useState(false)
|
||||
const [deleteModel, setDeleteModel] = useState<DeleteModel & { providerKey: ProviderEnum }>()
|
||||
const [modalMode, setModalMode] = useState('add')
|
||||
|
||||
let modelList = []
|
||||
|
||||
if (locale === 'en') {
|
||||
modelList = [
|
||||
config.azure_openai,
|
||||
config.replicate,
|
||||
config.huggingface_hub,
|
||||
config.cohere,
|
||||
config.zhipuai,
|
||||
config.baichuan,
|
||||
config.spark,
|
||||
config.minimax,
|
||||
config.tongyi,
|
||||
config.wenxin,
|
||||
config.jina,
|
||||
config.chatglm,
|
||||
config.xinference,
|
||||
config.openllm,
|
||||
config.localai,
|
||||
]
|
||||
}
|
||||
else {
|
||||
modelList = [
|
||||
config.huggingface_hub,
|
||||
config.cohere,
|
||||
config.zhipuai,
|
||||
config.spark,
|
||||
config.baichuan,
|
||||
config.minimax,
|
||||
config.azure_openai,
|
||||
config.replicate,
|
||||
config.tongyi,
|
||||
config.wenxin,
|
||||
config.jina,
|
||||
config.chatglm,
|
||||
config.xinference,
|
||||
config.openllm,
|
||||
config.localai,
|
||||
]
|
||||
}
|
||||
|
||||
const handleOpenModal = (newModelModalConfig: ProviderConfigModal | undefined, editValue?: FormValue) => {
|
||||
if (newModelModalConfig) {
|
||||
setShowModal(true)
|
||||
const defaultValue = editValue ? { ...newModelModalConfig.defaultValue, ...editValue } : newModelModalConfig.defaultValue
|
||||
setModelModalConfig({
|
||||
...newModelModalConfig,
|
||||
defaultValue,
|
||||
})
|
||||
if (editValue)
|
||||
setModalMode('edit')
|
||||
else
|
||||
setModalMode('add')
|
||||
}
|
||||
}
|
||||
const handleCancelModal = () => {
|
||||
setShowModal(false)
|
||||
}
|
||||
const handleUpdateProvidersAndModelList = () => {
|
||||
updateModelList(ModelType.textGeneration)
|
||||
updateModelList(ModelType.embeddings)
|
||||
updateModelList(ModelType.speech2text)
|
||||
updateModelList(ModelType.reranking)
|
||||
mutateProviders()
|
||||
}
|
||||
const handleSave = async (originValue?: FormValue) => {
|
||||
if (originValue && modelModalConfig) {
|
||||
const v = modelModalConfig.filterValue ? modelModalConfig.filterValue(originValue) : originValue
|
||||
let body, url
|
||||
if (ConfigurableProviders.includes(modelModalConfig.key)) {
|
||||
const { model_name, model_type, ...config } = v
|
||||
body = {
|
||||
model_name,
|
||||
model_type,
|
||||
config,
|
||||
}
|
||||
url = `/workspaces/current/model-providers/${modelModalConfig.key}/models`
|
||||
}
|
||||
else {
|
||||
body = {
|
||||
config: v,
|
||||
}
|
||||
url = `/workspaces/current/model-providers/${modelModalConfig.key}`
|
||||
}
|
||||
|
||||
try {
|
||||
eventEmitter?.emit('provider-save')
|
||||
const res = await setModelProvider({ url, body })
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
handleUpdateProvidersAndModelList()
|
||||
handleCancelModal()
|
||||
}
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
catch (e) {
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleConfirm = (deleteModel: DeleteModel, providerKey: ProviderEnum) => {
|
||||
setDeleteModel({ ...deleteModel, providerKey })
|
||||
setConfirmShow(true)
|
||||
}
|
||||
|
||||
const handleOperate = async ({ type, value }: Record<string, any>, provierKey: ProviderEnum) => {
|
||||
if (type === 'delete') {
|
||||
if (!value) {
|
||||
const res = await deleteModelProvider({ url: `/workspaces/current/model-providers/${provierKey}` })
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
handleUpdateProvidersAndModelList()
|
||||
}
|
||||
}
|
||||
else {
|
||||
handleConfirm(value, provierKey)
|
||||
}
|
||||
}
|
||||
|
||||
if (type === 'priority') {
|
||||
const res = await changeModelProviderPriority({
|
||||
url: `/workspaces/current/model-providers/${provierKey}/preferred-provider-type`,
|
||||
body: {
|
||||
preferred_provider_type: value,
|
||||
},
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
mutateProviders()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const handleDeleteModel = async () => {
|
||||
const { model_name, model_type, providerKey } = deleteModel || {}
|
||||
const res = await deleteModelProviderModel({
|
||||
url: `/workspaces/current/model-providers/${providerKey}/models?model_name=${model_name}&model_type=${model_type}`,
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
setConfirmShow(false)
|
||||
handleUpdateProvidersAndModelList()
|
||||
}
|
||||
}
|
||||
|
||||
const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel
|
||||
|
||||
return (
|
||||
<div className='relative pt-1 -mt-2'>
|
||||
<div className={`flex items-center justify-between mb-2 h-8 ${defaultModelNotConfigured && 'px-3 bg-[#FFFAEB] rounded-lg border border-[#FEF0C7]'}`}>
|
||||
{
|
||||
defaultModelNotConfigured
|
||||
? (
|
||||
<div className='flex items-center text-xs font-medium text-gray-700'>
|
||||
<AlertTriangle className='mr-1 w-3 h-3 text-[#F79009]' />
|
||||
{t('common.modelProvider.notConfigured')}
|
||||
</div>
|
||||
)
|
||||
: <div className='text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
|
||||
}
|
||||
<SystemModel onUpdate={() => mutateProviders()} />
|
||||
</div>
|
||||
<div className='grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6'>
|
||||
{
|
||||
MODEL_CARD_LIST.map((model, index) => (
|
||||
<ModelCard
|
||||
key={index}
|
||||
modelItem={model.item}
|
||||
currentProvider={providers?.[model.item.key]}
|
||||
onOpenModal={editValue => handleOpenModal(model.modal, editValue)}
|
||||
onOperate={v => handleOperate(v, model.item.key)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{
|
||||
modelList.map((model, index) => (
|
||||
<ModelItem
|
||||
key={index}
|
||||
modelItem={model.item}
|
||||
currentProvider={providers?.[model.item.key]}
|
||||
onOpenModal={editValue => handleOpenModal(model.modal, editValue)}
|
||||
onOperate={v => handleOperate(v, model.item.key)}
|
||||
onUpdate={mutateProviders}
|
||||
/>
|
||||
))
|
||||
}
|
||||
<ModelModal
|
||||
isShow={showModal}
|
||||
modelModal={modelModalConfig}
|
||||
onCancel={handleCancelModal}
|
||||
onSave={handleSave}
|
||||
mode={modalMode}
|
||||
/>
|
||||
<Confirm
|
||||
isShow={confirmShow}
|
||||
onCancel={() => setConfirmShow(false)}
|
||||
title={deleteModel?.model_name || ''}
|
||||
desc={t('common.modelProvider.item.deleteDesc', { modelName: deleteModel?.model_name }) || ''}
|
||||
onConfirm={handleDeleteModel}
|
||||
confirmWrapperClassName='!z-30'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelPage
|
||||
|
|
@ -1,117 +0,0 @@
|
|||
import { useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import type { Provider, ProviderWithQuota } from '../declarations'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { getPayUrl } from '@/service/common'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
type QuotaProps = {
|
||||
currentProvider: Provider
|
||||
}
|
||||
const Quota: FC<QuotaProps> = ({
|
||||
currentProvider,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const systemTrial = currentProvider.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota)?.quota_type === 'trial') as ProviderWithQuota
|
||||
const systemPaid = currentProvider.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota)?.quota_type === 'paid') as ProviderWithQuota
|
||||
const QUOTA_UNIT_MAP: Record<string, string> = {
|
||||
times: t('common.modelProvider.card.callTimes'),
|
||||
tokens: 'Tokens',
|
||||
}
|
||||
|
||||
const renderStatus = () => {
|
||||
const totalQuota = (systemPaid?.is_valid ? systemPaid.quota_limit : 0) + systemTrial.quota_limit
|
||||
const totalUsed = (systemPaid?.is_valid ? systemPaid.quota_used : 0) + systemTrial.quota_used
|
||||
|
||||
if (totalQuota === totalUsed) {
|
||||
return (
|
||||
<div className='px-1.5 bg-[#FEF3F2] rounded-md text-xs font-semibold text-[#D92D20]'>
|
||||
{t('common.modelProvider.card.quotaExhausted')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
if (systemPaid?.is_valid) {
|
||||
return (
|
||||
<div className='px-1.5 bg-[#FFF6ED] rounded-md text-xs font-semibold text-[#EC4A0A]'>
|
||||
{t('common.modelProvider.card.paid')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<div className='px-1.5 bg-primary-50 rounded-md text-xs font-semibold text-primary-600'>
|
||||
{t('common.modelProvider.card.onTrial')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderQuota = () => {
|
||||
if (systemPaid?.is_valid)
|
||||
return formatNumber(systemPaid.quota_limit - systemPaid.quota_used)
|
||||
|
||||
if (systemTrial.is_valid)
|
||||
return formatNumber(systemTrial.quota_limit - systemTrial.quota_used)
|
||||
}
|
||||
const renderUnit = () => {
|
||||
if (systemPaid?.is_valid)
|
||||
return QUOTA_UNIT_MAP[systemPaid.quota_unit]
|
||||
|
||||
if (systemTrial.is_valid)
|
||||
return QUOTA_UNIT_MAP[systemTrial.quota_unit]
|
||||
}
|
||||
const handleGetPayUrl = async () => {
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await getPayUrl(`/workspaces/current/model-providers/${systemPaid.provider_name}/checkout-url`)
|
||||
|
||||
window.location.href = res.url
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex justify-between px-4 py-3 border-b-[0.5px] border-b-[rgba(0, 0, 0, 0.5)]'>
|
||||
<div>
|
||||
<div className='flex items-center mb-1 h-5'>
|
||||
<div className='mr-1 text-xs font-medium text-gray-500'>
|
||||
{t('common.modelProvider.card.quota')}
|
||||
</div>
|
||||
{renderStatus()}
|
||||
</div>
|
||||
<div className='flex items-center text-gray-700'>
|
||||
<div className='mr-1 text-sm font-medium'>{renderQuota()}</div>
|
||||
<div className='mr-1 text-sm'>
|
||||
{renderUnit()}
|
||||
</div>
|
||||
<Tooltip
|
||||
selector='setting-model-card'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.card.tip')}</div>
|
||||
}
|
||||
>
|
||||
<InfoCircle className='w-3 h-3 text-gray-400 hover:text-gray-700' />
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
systemPaid && (
|
||||
<Button
|
||||
type='primary'
|
||||
className='mt-1.5 !px-3 !h-8 !text-[13px] font-medium !rounded-lg'
|
||||
onClick={handleGetPayUrl}
|
||||
disabled={loading}
|
||||
>
|
||||
{t('common.modelProvider.card.buyQuota')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Quota
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type {
|
||||
FormValue,
|
||||
Provider,
|
||||
ProviderConfigItem,
|
||||
ProviderWithConfig,
|
||||
} from '../declarations'
|
||||
import Indicator from '../../../indicator'
|
||||
import Selector from '../selector'
|
||||
import Quota from './Quota'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
import I18n from '@/context/i18n'
|
||||
import { Plus } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
type ModelCardProps = {
|
||||
currentProvider?: Provider
|
||||
modelItem: ProviderConfigItem
|
||||
onOpenModal: (v?: FormValue) => void
|
||||
onOperate: (v: Record<string, any>) => void
|
||||
}
|
||||
|
||||
const ModelCard: FC<ModelCardProps> = ({
|
||||
currentProvider,
|
||||
modelItem,
|
||||
onOpenModal,
|
||||
onOperate,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithConfig
|
||||
|
||||
return (
|
||||
<div className='rounded-xl border-[0.5px] border-gray-200 shadow-xs'>
|
||||
<div className={`flex px-4 pt-4 pb-3 rounded-t-lg ${modelItem.bgColor}`}>
|
||||
<div className='grow mr-3'>
|
||||
<div className='mb-1'>
|
||||
{modelItem.titleIcon[locale]}
|
||||
</div>
|
||||
<div className='h-9 text-xs text-black opacity-60'>{modelItem.desc?.[locale]}</div>
|
||||
</div>
|
||||
{modelItem.subTitleIcon}
|
||||
</div>
|
||||
{
|
||||
!IS_CE_EDITION && currentProvider && <Quota currentProvider={currentProvider} />
|
||||
}
|
||||
{
|
||||
custom?.is_valid
|
||||
? (
|
||||
<div className='flex items-center px-4 h-12'>
|
||||
<Indicator color='green' className='mr-2' />
|
||||
<div className='grow text-[13px] font-medium text-gray-700'>API key</div>
|
||||
<div
|
||||
className='mr-1 px-2 leading-6 rounded-md text-xs font-medium text-gray-500 hover:bg-gray-50 cursor-pointer'
|
||||
onClick={() => onOpenModal(custom?.config)}
|
||||
>
|
||||
{t('common.operation.edit')}
|
||||
</div>
|
||||
<Selector
|
||||
onOperate={onOperate}
|
||||
value={currentProvider?.preferred_provider_type}
|
||||
hiddenOptions={IS_CE_EDITION}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div
|
||||
className='inline-flex items-center px-4 h-12 text-gray-500 cursor-pointer hover:text-primary-600'
|
||||
onClick={() => onOpenModal()}
|
||||
>
|
||||
<Plus className='mr-1.5 w-4 h-4'/>
|
||||
<div className='text-xs font-medium'>{t('common.modelProvider.addApiKey')}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelCard
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Indicator from '../../../indicator'
|
||||
import Selector from '../selector'
|
||||
import type { Model, ProviderEnum } from '../declarations'
|
||||
import { ProviderEnum as ProviderEnumValue } from '../declarations'
|
||||
import Button from '@/app/components/base/button'
|
||||
|
||||
type CardProps = {
|
||||
providerType: ProviderEnum
|
||||
models: Model[]
|
||||
onOpenModal: (v: Omit<Model, 'config'> & Model['config']) => void
|
||||
onOperate: (v: Record<string, any>) => void
|
||||
}
|
||||
|
||||
const Card: FC<CardProps> = ({
|
||||
providerType,
|
||||
models,
|
||||
onOpenModal,
|
||||
onOperate,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const renderDesc = (model: Model) => {
|
||||
if (providerType === ProviderEnumValue.azure_openai)
|
||||
return model.config.openai_api_base
|
||||
if (providerType === ProviderEnumValue.replicate)
|
||||
return `version: ${model.config.model_version}`
|
||||
if (providerType === ProviderEnumValue.huggingface_hub)
|
||||
return model.config.huggingfacehub_endpoint_url
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='px-3 pb-3'>
|
||||
{
|
||||
models.map(model => (
|
||||
<div key={`${model.model_name}-${model.model_type}`} className='flex mb-1 px-3 py-2 bg-white rounded-lg shadow-xs last:mb-0'>
|
||||
<div className='grow'>
|
||||
<div className='flex items-center mb-0.5 h-[18px] text-[13px] font-medium text-gray-700'>
|
||||
{model.model_name}
|
||||
<div className='ml-2 px-1.5 rounded-md border border-[rgba(0,0,0,0.08)] text-xs text-gray-600'>{model.model_type}</div>
|
||||
</div>
|
||||
<div className='text-xs text-gray-500'>
|
||||
{renderDesc(model)}
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Indicator className='mr-3' />
|
||||
<Button
|
||||
className='mr-1 !px-3 !h-7 rounded-md bg-white !text-xs font-medium text-gray-700'
|
||||
onClick={() => onOpenModal({ model_name: model.model_name, model_type: model.model_type, ...model.config })}
|
||||
>
|
||||
{t('common.operation.edit')}
|
||||
</Button>
|
||||
<Selector
|
||||
hiddenOptions
|
||||
onOperate={v => onOperate({ ...v, value: model })}
|
||||
className={open => `${open && '!bg-gray-100 shadow-none'} flex justify-center items-center w-7 h-7 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100`}
|
||||
deleteText={t('common.operation.remove') || ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Card
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
import { useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type { ProviderConfigItem, TypeWithI18N } from '../declarations'
|
||||
import { ProviderEnum as ProviderEnumValue } from '../declarations'
|
||||
import s from './index.module.css'
|
||||
import I18n from '@/context/i18n'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { submitFreeQuota } from '@/service/common'
|
||||
import { LinkExternal01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
const TIP_MAP: { [k: string]: TypeWithI18N } = {
|
||||
[ProviderEnumValue.minimax]: {
|
||||
'en': 'Earn 1 million tokens for free',
|
||||
'zh-Hans': '免费获取 100 万个 token',
|
||||
},
|
||||
[ProviderEnumValue.spark]: {
|
||||
'en': 'Earn 3 million tokens (v3.0) for free',
|
||||
'zh-Hans': '免费获取 300 万个 token (v3.0)',
|
||||
},
|
||||
[ProviderEnumValue.zhipuai]: {
|
||||
'en': 'Earn 10 million tokens for free',
|
||||
'zh-Hans': '免费获取 1000 万个 token',
|
||||
},
|
||||
}
|
||||
type FreeQuotaProps = {
|
||||
modelItem: ProviderConfigItem
|
||||
onUpdate: () => void
|
||||
}
|
||||
const FreeQuota: FC<FreeQuotaProps> = ({
|
||||
modelItem,
|
||||
onUpdate,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleClick = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const res = await submitFreeQuota(`/workspaces/current/model-providers/${modelItem.key}/free-quota-submit`)
|
||||
|
||||
if (res.type === 'redirect' && res.redirect_url)
|
||||
window.location.href = res.redirect_url
|
||||
else if (res.type === 'submit' && res.result === 'success')
|
||||
onUpdate()
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
📣
|
||||
<div className={`${s.vender} ml-1 text-xs font-medium text-transparent`}>{TIP_MAP[modelItem.key][locale]}</div>
|
||||
<div className='mx-1 text-xs font-medium text-gray-400'>·</div>
|
||||
<a
|
||||
href='https://docs.dify.ai/v/zh-hans/getting-started/faq/llms-use-faq#8.-ru-he-mian-fei-shen-ling-xun-fei-xing-huo-minimax-mo-xing-de-ti-yanedu'
|
||||
target='_blank'
|
||||
className='flex items-center text-xs font-medium text-[#155EEF]'>
|
||||
{t('common.modelProvider.freeQuota.howToEarn')}
|
||||
<LinkExternal01 className='ml-0.5 w-3 h-3' />
|
||||
</a>
|
||||
<Button
|
||||
type='primary'
|
||||
className='ml-3 !px-3 !h-7 !rounded-md !text-xs !font-medium'
|
||||
onClick={handleClick}
|
||||
disabled={loading}
|
||||
>
|
||||
{t('common.operation.getForFree')}
|
||||
</Button>
|
||||
<div className='mx-2 w-[1px] h-4 bg-black/5' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FreeQuota
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
|
||||
type QuotaCardProps = {
|
||||
remainTokens: number
|
||||
}
|
||||
|
||||
const QuotaCard: FC<QuotaCardProps> = ({
|
||||
remainTokens,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<div className='px-3 pb-3'>
|
||||
<div className='px-3 py-2 bg-white rounded-lg shadow-xs last:mb-0'>
|
||||
<div className='flex items-center h-[18px] text-xs font-medium text-gray-500'>
|
||||
{t('common.modelProvider.item.freeQuota')}
|
||||
</div>
|
||||
<div className='flex items-center h-5 text-sm font-medium text-gray-700'>
|
||||
{formatNumber(remainTokens)}
|
||||
<div className='ml-1 font-normal'>Tokens</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default QuotaCard
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type { FormValue, Provider, ProviderConfigItem, ProviderWithConfig, ProviderWithQuota } from '../declarations'
|
||||
import { ProviderEnum } from '../declarations'
|
||||
import Indicator from '../../../indicator'
|
||||
import Selector from '../selector'
|
||||
import FreeQuota from './FreeQuota'
|
||||
import I18n from '@/context/i18n'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
type SettingProps = {
|
||||
currentProvider?: Provider
|
||||
modelItem: ProviderConfigItem
|
||||
onOpenModal: (v?: FormValue) => void
|
||||
onOperate: (v: Record<string, any>) => void
|
||||
onUpdate: () => void
|
||||
}
|
||||
|
||||
const Setting: FC<SettingProps> = ({
|
||||
currentProvider,
|
||||
modelItem,
|
||||
onOpenModal,
|
||||
onOperate,
|
||||
onUpdate,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const configurable = currentProvider?.model_flexibility === 'configurable'
|
||||
const systemFree = currentProvider?.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota).quota_type === 'free') as ProviderWithQuota
|
||||
const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithConfig
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
(modelItem.key === ProviderEnum.minimax || modelItem.key === ProviderEnum.spark || modelItem.key === ProviderEnum.zhipuai) && systemFree && !systemFree?.is_valid && !IS_CE_EDITION && locale === 'zh-Hans' && (
|
||||
<FreeQuota
|
||||
modelItem={modelItem}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
modelItem.disable && !IS_CE_EDITION && (
|
||||
<div className='flex items-center text-xs text-gray-500'>
|
||||
{modelItem.disable.tip[locale]}
|
||||
<a
|
||||
className={`${locale === 'en' && 'ml-1'} text-primary-600 cursor-pointer`}
|
||||
href={modelItem.disable.link.href[locale]}
|
||||
target='_blank'
|
||||
>
|
||||
{modelItem.disable.link.label[locale]}
|
||||
</a>
|
||||
<div className='mx-2 w-[1px] h-4 bg-black/5' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
configurable && (
|
||||
<Button
|
||||
className={`!px-3 !h-7 rounded-md bg-white !text-xs font-medium text-gray-700 ${!!modelItem.disable && !IS_CE_EDITION && '!text-gray-300'}`}
|
||||
onClick={() => onOpenModal()}
|
||||
disabled={!!modelItem.disable && !IS_CE_EDITION}
|
||||
>
|
||||
{t('common.operation.add')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
{
|
||||
!configurable && custom?.config && (
|
||||
<div className='flex items-center'>
|
||||
<Indicator className='mr-3' />
|
||||
<Button
|
||||
className='mr-1 !px-3 !h-7 rounded-md bg-white !text-xs font-medium text-gray-700'
|
||||
onClick={() => onOpenModal(custom.config)}
|
||||
>
|
||||
{t('common.operation.edit')}
|
||||
</Button>
|
||||
<Selector
|
||||
hiddenOptions={!systemFree?.is_valid || IS_CE_EDITION}
|
||||
value={currentProvider?.preferred_provider_type}
|
||||
onOperate={onOperate}
|
||||
className={open => `${open && '!bg-gray-100 shadow-none'} flex justify-center items-center w-7 h-7 bg-white rounded-md border-[0.5px] border-gray-200 shadow-xs cursor-pointer hover:bg-gray-100`}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!configurable && !custom?.config && (
|
||||
<Button
|
||||
className={`!px-3 !h-7 rounded-md bg-white !text-xs font-medium text-gray-700 ${!!modelItem.disable && !IS_CE_EDITION && '!text-gray-300'}`}
|
||||
onClick={() => onOpenModal()}
|
||||
disabled={!!modelItem.disable && !IS_CE_EDITION}
|
||||
>
|
||||
{t('common.operation.setup')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Setting
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type {
|
||||
FormValue,
|
||||
Provider,
|
||||
ProviderConfigItem,
|
||||
ProviderWithModels,
|
||||
ProviderWithQuota,
|
||||
} from '../declarations'
|
||||
import Setting from './Setting'
|
||||
import Card from './Card'
|
||||
import QuotaCard from './QuotaCard'
|
||||
import I18n from '@/context/i18n'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
type ModelItemProps = {
|
||||
currentProvider?: Provider
|
||||
modelItem: ProviderConfigItem
|
||||
onOpenModal: (v?: FormValue) => void
|
||||
onOperate: (v: Record<string, any>) => void
|
||||
onUpdate: () => void
|
||||
}
|
||||
|
||||
const ModelItem: FC<ModelItemProps> = ({
|
||||
currentProvider,
|
||||
modelItem,
|
||||
onOpenModal,
|
||||
onOperate,
|
||||
onUpdate,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const custom = currentProvider?.providers.find(p => p.provider_type === 'custom') as ProviderWithModels
|
||||
const systemFree = currentProvider?.providers.find(p => p.provider_type === 'system' && (p as ProviderWithQuota).quota_type === 'free') as ProviderWithQuota
|
||||
|
||||
return (
|
||||
<div className='mb-2 bg-gray-50 rounded-xl'>
|
||||
<div className='flex justify-between items-center p-4 min-h-[56px] flex-wrap gap-y-1'>
|
||||
<div className='flex items-center'>
|
||||
{modelItem.titleIcon[locale]}
|
||||
{
|
||||
modelItem.hit && (
|
||||
<div className='ml-2 text-xs text-gray-500'>{modelItem.hit[locale]}</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<Setting
|
||||
currentProvider={currentProvider}
|
||||
modelItem={modelItem}
|
||||
onOpenModal={onOpenModal}
|
||||
onOperate={onOperate}
|
||||
onUpdate={onUpdate}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
!!custom?.models?.length && (
|
||||
<Card
|
||||
providerType={modelItem.key}
|
||||
models={custom?.models}
|
||||
onOpenModal={onOpenModal}
|
||||
onOperate={onOperate}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
systemFree?.is_valid && !IS_CE_EDITION && (
|
||||
<QuotaCard remainTokens={systemFree.quota_limit - systemFree.quota_used}/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelItem
|
||||
|
|
@ -1,208 +0,0 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import type { Dispatch, FC, SetStateAction } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { type Field, type FormValue, type ProviderConfigModal, ProviderEnum } from '../declarations'
|
||||
import { useValidate } from '../../key-validator/hooks'
|
||||
import { ValidatingTip } from '../../key-validator/ValidateStatus'
|
||||
import { validateModelProviderFn } from '../utils'
|
||||
import Input from './Input'
|
||||
import I18n from '@/context/i18n'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
|
||||
type FormProps = {
|
||||
modelModal?: ProviderConfigModal
|
||||
initValue?: FormValue
|
||||
fields: Field[]
|
||||
onChange: (v: FormValue) => void
|
||||
onValidatedError: (v: string) => void
|
||||
mode: string
|
||||
cleared: boolean
|
||||
onClearedChange: Dispatch<SetStateAction<boolean>>
|
||||
onValidating: (validating: boolean) => void
|
||||
}
|
||||
|
||||
const nameClassName = `
|
||||
py-2 text-sm text-gray-900
|
||||
`
|
||||
|
||||
const Form: FC<FormProps> = ({
|
||||
modelModal,
|
||||
initValue = {},
|
||||
fields,
|
||||
onChange,
|
||||
onValidatedError,
|
||||
mode,
|
||||
cleared,
|
||||
onClearedChange,
|
||||
onValidating,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const [value, setValue] = useState(initValue)
|
||||
const [validate, validating, validatedStatusState] = useValidate(value)
|
||||
const [changeKey, setChangeKey] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
onValidatedError(validatedStatusState.message || '')
|
||||
}, [validatedStatusState, onValidatedError])
|
||||
useEffect(() => {
|
||||
onValidating(validating)
|
||||
}, [validating, onValidating])
|
||||
|
||||
const updateValue = (v: FormValue) => {
|
||||
setValue(v)
|
||||
onChange(v)
|
||||
}
|
||||
|
||||
const handleMultiFormChange = (v: FormValue, newChangeKey: string) => {
|
||||
updateValue(v)
|
||||
setChangeKey(newChangeKey)
|
||||
|
||||
const validateKeys = (typeof modelModal?.validateKeys === 'function' ? modelModal?.validateKeys(v) : modelModal?.validateKeys) || []
|
||||
if (validateKeys.length) {
|
||||
validate({
|
||||
before: () => {
|
||||
for (let i = 0; i < validateKeys.length; i++) {
|
||||
if (!v[validateKeys[i]])
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
run: () => {
|
||||
return validateModelProviderFn(modelModal!.key, modelModal?.filterValue ? modelModal?.filterValue(v) : v)
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const handleClear = (saveValue?: FormValue) => {
|
||||
const needClearFields = modelModal?.fields.filter(field => field.type !== 'radio')
|
||||
const newValue: Record<string, string> = {}
|
||||
needClearFields?.forEach((field) => {
|
||||
newValue[field.key] = ''
|
||||
})
|
||||
updateValue({ ...value, ...newValue, ...saveValue })
|
||||
onClearedChange(true)
|
||||
}
|
||||
|
||||
const handleFormChange = (k: string, v: string) => {
|
||||
if (mode === 'edit' && !cleared) {
|
||||
handleClear({ [k]: v })
|
||||
}
|
||||
else {
|
||||
const extraValue: Record<string, string> = {}
|
||||
if (
|
||||
(
|
||||
(k === 'model_type' && v === 'embeddings' && value.huggingfacehub_api_type === 'inference_endpoints')
|
||||
|| (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'embeddings')
|
||||
)
|
||||
&& modelModal?.key === ProviderEnum.huggingface_hub
|
||||
)
|
||||
extraValue.task_type = 'feature-extraction'
|
||||
|
||||
if (
|
||||
(
|
||||
(k === 'model_type' && v === 'text-generation' && value.huggingfacehub_api_type === 'inference_endpoints')
|
||||
|| (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'text-generation')
|
||||
)
|
||||
&& modelModal?.key === ProviderEnum.huggingface_hub
|
||||
)
|
||||
extraValue.task_type = 'text-generation'
|
||||
|
||||
if (
|
||||
(
|
||||
(k === 'model_type' && v === 'chat' && value.huggingfacehub_api_type === 'inference_endpoints')
|
||||
|| (k === 'huggingfacehub_api_type' && v === 'inference_endpoints' && value.model_type === 'chat')
|
||||
)
|
||||
&& modelModal?.key === ProviderEnum.huggingface_hub
|
||||
)
|
||||
extraValue.task_type = 'question-answer'
|
||||
|
||||
handleMultiFormChange({ ...value, [k]: v, ...extraValue }, k)
|
||||
}
|
||||
}
|
||||
|
||||
const handleFocus = () => {
|
||||
if (mode === 'edit' && !cleared)
|
||||
handleClear()
|
||||
}
|
||||
|
||||
const renderField = (field: Field) => {
|
||||
const hidden = typeof field.hidden === 'function' ? field.hidden(value) : field.hidden
|
||||
|
||||
if (hidden)
|
||||
return null
|
||||
|
||||
if (field.type === 'text') {
|
||||
return (
|
||||
<div key={field.key} className='py-3'>
|
||||
<div className={nameClassName}>{field.label[locale]}</div>
|
||||
<Input
|
||||
field={field}
|
||||
value={value}
|
||||
onChange={v => handleMultiFormChange(v, field.key)}
|
||||
onFocus={handleFocus}
|
||||
validatedStatusState={validatedStatusState}
|
||||
/>
|
||||
{validating && changeKey === field.key && <ValidatingTip />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (field.type === 'radio') {
|
||||
const options = typeof field.options === 'function' ? field.options(value) : field.options
|
||||
|
||||
return (
|
||||
<div key={field.key} className='py-3'>
|
||||
<div className={nameClassName}>{field.label[locale]}</div>
|
||||
<div className={`grid grid-cols-${options?.length} gap-3`}>
|
||||
{
|
||||
options?.map(option => (
|
||||
<div
|
||||
className={`
|
||||
flex items-center px-3 py-2 rounded-lg border border-gray-100 bg-gray-25 cursor-pointer
|
||||
${value?.[field.key] === option.key && 'bg-white border-[1.5px] border-primary-400 shadow-sm'}
|
||||
`}
|
||||
onClick={() => handleFormChange(field.key, option.key)}
|
||||
key={`${field.key}-${option.key}`}
|
||||
>
|
||||
<div className={`
|
||||
flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full
|
||||
${value?.[field.key] === option.key && 'border-[5px] border-primary-600'}
|
||||
`} />
|
||||
<div className='text-sm text-gray-900'>{option.label[locale]}</div>
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
{validating && changeKey === field.key && <ValidatingTip />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (field.type === 'select') {
|
||||
const options = typeof field.options === 'function' ? field.options(value) : field.options
|
||||
|
||||
return (
|
||||
<div key={field.key} className='py-3'>
|
||||
<div className={nameClassName}>{field.label[locale]}</div>
|
||||
<SimpleSelect
|
||||
defaultValue={value[field.key]}
|
||||
items={options!.map(option => ({ value: option.key, name: option.label[locale] }))}
|
||||
onSelect={item => handleFormChange(field.key, item.value as string)}
|
||||
/>
|
||||
{validating && changeKey === field.key && <ValidatingTip />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
fields.map(field => renderField(field))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type { Field, FormValue } from '../declarations'
|
||||
import { ValidatedSuccessIcon } from '../../key-validator/ValidateStatus'
|
||||
import { ValidatedStatus } from '../../key-validator/declarations'
|
||||
import type { ValidatedStatusState } from '../../key-validator/declarations'
|
||||
import I18n from '@/context/i18n'
|
||||
|
||||
type InputProps = {
|
||||
field: Field
|
||||
value: FormValue
|
||||
onChange: (v: FormValue) => void
|
||||
onFocus: () => void
|
||||
validatedStatusState: ValidatedStatusState
|
||||
}
|
||||
const Input: FC<InputProps> = ({
|
||||
field,
|
||||
value,
|
||||
onChange,
|
||||
onFocus,
|
||||
validatedStatusState,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const showValidatedIcon = validatedStatusState.status === ValidatedStatus.Success && value[field.key]
|
||||
|
||||
const getValidatedIcon = () => {
|
||||
if (showValidatedIcon)
|
||||
return <div className='absolute top-2.5 right-2.5'><ValidatedSuccessIcon /></div>
|
||||
}
|
||||
|
||||
const handleChange = (v: string) => {
|
||||
const newFormValue = { ...value, [field.key]: v }
|
||||
onChange(newFormValue)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative'>
|
||||
<input
|
||||
tabIndex={-1}
|
||||
className={`
|
||||
block px-3 w-full h-9 bg-gray-100 text-sm rounded-lg border border-transparent
|
||||
appearance-none outline-none caret-primary-600
|
||||
hover:border-[rgba(0,0,0,0.08)] hover:bg-gray-50
|
||||
focus:bg-white focus:border-gray-300 focus:shadow-xs
|
||||
placeholder:text-sm placeholder:text-gray-400
|
||||
${showValidatedIcon && 'pr-[30px]'}
|
||||
`}
|
||||
placeholder={field?.placeholder?.[locale] || ''}
|
||||
onChange={e => handleChange(e.target.value)}
|
||||
onFocus={onFocus}
|
||||
value={value[field.key] || ''}
|
||||
/>
|
||||
{getValidatedIcon()}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Input
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
import { useCallback, useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type { FormValue, ProviderConfigModal } from '../declarations'
|
||||
import { ConfigurableProviders } from '../utils'
|
||||
import Form from './Form'
|
||||
import I18n from '@/context/i18n'
|
||||
import Button from '@/app/components/base/button'
|
||||
import { Lock01 } from '@/app/components/base/icons/src/vender/solid/security'
|
||||
import { LinkExternal02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
type ModelModalProps = {
|
||||
isShow: boolean
|
||||
onCancel: () => void
|
||||
modelModal?: ProviderConfigModal
|
||||
onSave: (v?: FormValue) => void
|
||||
mode: string
|
||||
}
|
||||
|
||||
const ModelModal: FC<ModelModalProps> = ({
|
||||
isShow,
|
||||
onCancel,
|
||||
modelModal,
|
||||
onSave,
|
||||
mode,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { locale } = useContext(I18n)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [value, setValue] = useState<FormValue | undefined>()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [errorMessage, setErrorMessage] = useState('')
|
||||
const [cleared, setCleared] = useState(false)
|
||||
const [prevIsShow, setPrevIsShow] = useState(isShow)
|
||||
const [validating, setValidating] = useState(false)
|
||||
|
||||
if (prevIsShow !== isShow) {
|
||||
setCleared(false)
|
||||
setPrevIsShow(isShow)
|
||||
}
|
||||
|
||||
eventEmitter?.useSubscription((v) => {
|
||||
if (v === 'provider-save')
|
||||
setLoading(true)
|
||||
else
|
||||
setLoading(false)
|
||||
})
|
||||
const handleValidatedError = useCallback((newErrorMessage: string) => {
|
||||
setErrorMessage(newErrorMessage)
|
||||
}, [])
|
||||
const handleValidating = useCallback((newValidating: boolean) => {
|
||||
setValidating(newValidating)
|
||||
}, [])
|
||||
const validateRequiredValue = () => {
|
||||
const validateValue = value || modelModal?.defaultValue
|
||||
if (modelModal) {
|
||||
const { fields } = modelModal
|
||||
const requiredFields = fields.filter(field => !(typeof field.hidden === 'function' ? field.hidden(validateValue) : field.hidden) && field.required)
|
||||
|
||||
for (let i = 0; i < requiredFields.length; i++) {
|
||||
const currentField = requiredFields[i]
|
||||
if (!validateValue?.[currentField.key]) {
|
||||
setErrorMessage(t('appDebug.errorMessage.valueOfVarRequired', { key: currentField.label[locale] }) || '')
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
const handleSave = () => {
|
||||
if (validateRequiredValue())
|
||||
onSave(value || modelModal?.defaultValue)
|
||||
}
|
||||
|
||||
const renderTitlePrefix = () => {
|
||||
let prefix
|
||||
if (mode === 'edit')
|
||||
prefix = t('common.operation.edit')
|
||||
else
|
||||
prefix = ConfigurableProviders.includes(modelModal!.key) ? t('common.operation.create') : t('common.operation.setup')
|
||||
|
||||
return `${prefix} ${modelModal?.title[locale]}`
|
||||
}
|
||||
|
||||
if (!isShow)
|
||||
return null
|
||||
|
||||
return (
|
||||
<PortalToFollowElem open>
|
||||
<PortalToFollowElemContent className='w-full h-full z-[60]'>
|
||||
<div className='fixed inset-0 flex items-center justify-center bg-black/[.25]'>
|
||||
<div className='mx-2 w-[640px] max-h-[calc(100vh-120px)] bg-white shadow-xl rounded-2xl overflow-y-auto'>
|
||||
<div className='px-8 pt-8'>
|
||||
<div className='flex justify-between items-center mb-2'>
|
||||
<div className='text-xl font-semibold text-gray-900'>{renderTitlePrefix()}</div>
|
||||
{modelModal?.icon}
|
||||
</div>
|
||||
<Form
|
||||
modelModal={modelModal}
|
||||
fields={modelModal?.fields || []}
|
||||
initValue={modelModal?.defaultValue}
|
||||
onChange={newValue => setValue(newValue)}
|
||||
onValidatedError={handleValidatedError}
|
||||
mode={mode}
|
||||
cleared={cleared}
|
||||
onClearedChange={setCleared}
|
||||
onValidating={handleValidating}
|
||||
/>
|
||||
<div className='flex justify-between items-center py-6 flex-wrap gap-y-2'>
|
||||
<a
|
||||
href={modelModal?.link.href}
|
||||
target='_blank'
|
||||
className='inline-flex items-center text-xs text-primary-600'
|
||||
>
|
||||
{modelModal?.link.label[locale]}
|
||||
<LinkExternal02 className='ml-1 w-3 h-3' />
|
||||
</a>
|
||||
<div>
|
||||
<Button className='mr-2 !h-9 !text-sm font-medium text-gray-700' onClick={onCancel}>{t('common.operation.cancel')}</Button>
|
||||
<Button
|
||||
className='!h-9 !text-sm font-medium'
|
||||
type='primary'
|
||||
onClick={handleSave}
|
||||
disabled={loading || (mode === 'edit' && !cleared) || validating}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='border-t-[0.5px] border-t-[rgba(0,0,0,0.05)]'>
|
||||
{
|
||||
errorMessage
|
||||
? (
|
||||
<div className='flex px-[10px] py-3 bg-[#FEF3F2] text-xs text-[#D92D20]'>
|
||||
<AlertCircle className='mt-[1px] mr-2 w-[14px] h-[14px]' />
|
||||
{errorMessage}
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className='flex justify-center items-center py-3 bg-gray-50 text-xs text-gray-500'>
|
||||
<Lock01 className='mr-1 w-3 h-3 text-gray-500' />
|
||||
{t('common.modelProvider.encrypted.front')}
|
||||
<a
|
||||
className='text-primary-600 mx-1'
|
||||
target={'_blank'}
|
||||
href='https://pycryptodome.readthedocs.io/en/latest/src/cipher/oaep.html'
|
||||
>
|
||||
PKCS1_OAEP
|
||||
</a>
|
||||
{t('common.modelProvider.encrypted.back')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelModal
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import React, { Fragment, useEffect, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import _ from 'lodash-es'
|
||||
import cn from 'classnames'
|
||||
import ModelModal from '../model-modal'
|
||||
import cohereConfig from '../configs/cohere'
|
||||
import s from './style.module.css'
|
||||
import type { BackendModel, FormValue, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
||||
import ProviderName from '@/app/components/app/configuration/config-model/provider-name'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { fetchDefaultModal, setModelProvider } from '@/service/common'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
|
||||
type Props = {
|
||||
value: {
|
||||
providerName: ProviderEnum
|
||||
modelName: string
|
||||
} | undefined
|
||||
modelType: ModelType
|
||||
isShowModelModeType?: boolean
|
||||
isShowAddModel?: boolean
|
||||
supportAgentThought?: boolean
|
||||
onChange: (value: BackendModel) => void
|
||||
popClassName?: string
|
||||
readonly?: boolean
|
||||
triggerIconSmall?: boolean
|
||||
whenEmptyGoToSetting?: boolean
|
||||
onUpdate?: () => void
|
||||
}
|
||||
|
||||
type ModelOption = {
|
||||
type: 'model'
|
||||
value: string
|
||||
providerName: ProviderEnum
|
||||
modelDisplayName: string
|
||||
model_mode: ModelModeType
|
||||
} | {
|
||||
type: 'provider'
|
||||
value: ProviderEnum
|
||||
}
|
||||
|
||||
const ModelSelector: FC<Props> = ({
|
||||
value,
|
||||
modelType,
|
||||
isShowModelModeType,
|
||||
isShowAddModel,
|
||||
supportAgentThought,
|
||||
onChange,
|
||||
popClassName,
|
||||
readonly,
|
||||
triggerIconSmall,
|
||||
whenEmptyGoToSetting,
|
||||
onUpdate,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const {
|
||||
textGenerationModelList,
|
||||
embeddingsModelList,
|
||||
speech2textModelList,
|
||||
rerankModelList,
|
||||
agentThoughtModelList,
|
||||
updateModelList,
|
||||
} = useProviderContext()
|
||||
const [search, setSearch] = useState('')
|
||||
const modelList = supportAgentThought
|
||||
? agentThoughtModelList
|
||||
: ({
|
||||
[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 = (() => {
|
||||
if (!search)
|
||||
return {}
|
||||
|
||||
const res: Record<string, string> = {}
|
||||
modelList.forEach(({ model_name, model_display_name }) => {
|
||||
res[model_name] = model_display_name
|
||||
})
|
||||
return res
|
||||
})()
|
||||
const filteredModelList = search
|
||||
? modelList.filter(({ model_name }) => {
|
||||
if (allModelNames[model_name].includes(search))
|
||||
return true
|
||||
|
||||
return false
|
||||
})
|
||||
: modelList
|
||||
|
||||
const hasRemoved = (value && value.modelName && value.providerName) && !modelList.find(({ model_name, model_provider }) => model_name === value.modelName && model_provider.provider_name === value.providerName)
|
||||
|
||||
const modelOptions: ModelOption[] = (() => {
|
||||
const providers = _.uniq(filteredModelList.map(item => item.model_provider.provider_name))
|
||||
const res: ModelOption[] = []
|
||||
providers.forEach((providerName) => {
|
||||
res.push({
|
||||
type: 'provider',
|
||||
value: providerName,
|
||||
})
|
||||
const models = filteredModelList.filter(m => m.model_provider.provider_name === providerName)
|
||||
models.forEach(({ model_name, model_display_name, model_mode }) => {
|
||||
res.push({
|
||||
type: 'model',
|
||||
providerName,
|
||||
value: model_name,
|
||||
modelDisplayName: model_display_name,
|
||||
model_mode,
|
||||
})
|
||||
})
|
||||
})
|
||||
return res
|
||||
})()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [showRerankModal, setShowRerankModal] = useState(false)
|
||||
const [shouldFetchRerankDefaultModel, setShouldFetchRerankDefaultModel] = useState(false)
|
||||
const { notify } = useToastContext()
|
||||
const { data: rerankDefaultModel } = useSWR(shouldFetchRerankDefaultModel ? '/workspaces/current/default-model?model_type=reranking' : null, fetchDefaultModal)
|
||||
const handleOpenRerankModal = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
setShowRerankModal(true)
|
||||
}
|
||||
const handleRerankModalSave = async (originValue?: FormValue) => {
|
||||
if (originValue) {
|
||||
try {
|
||||
eventEmitter?.emit('provider-save')
|
||||
const res = await setModelProvider({
|
||||
url: `/workspaces/current/model-providers/${cohereConfig.modal.key}`,
|
||||
body: {
|
||||
config: originValue,
|
||||
},
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
updateModelList(ModelType.reranking)
|
||||
setShowRerankModal(false)
|
||||
setShouldFetchRerankDefaultModel(true)
|
||||
if (onUpdate)
|
||||
onUpdate()
|
||||
}
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
catch (e) {
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (rerankDefaultModel && whenEmptyGoToSetting)
|
||||
onChange(rerankDefaultModel)
|
||||
}, [rerankDefaultModel])
|
||||
|
||||
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 opacity-50' : 'bg-gray-100', hasRemoved && '!bg-[#FEF3F2]')}>
|
||||
{
|
||||
({ open }) => (
|
||||
<>
|
||||
{
|
||||
(value && value.modelName && value.providerName)
|
||||
? (
|
||||
<>
|
||||
<ModelIcon
|
||||
className={cn('mr-1.5', !triggerIconSmall && 'w-5 h-5')}
|
||||
modelId={value.modelName}
|
||||
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 || value.modelName} />
|
||||
{isShowModelModeType && (
|
||||
<ModelModeTypeLabel className='ml-2' type={currModel?.model_mode as ModelModeType} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
: whenEmptyGoToSetting
|
||||
? (
|
||||
<div className='grow flex items-center h-9 justify-between' onClick={handleOpenRerankModal}>
|
||||
<div className='flex items-center text-[13px] font-medium text-primary-500'>
|
||||
<CubeOutline className='mr-1.5 w-4 h-4' />
|
||||
{t('common.modelProvider.selector.rerankTip')}
|
||||
</div>
|
||||
<LinkExternal01 className='w-3 h-3 text-gray-500' />
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className='grow text-left text-sm text-gray-800 opacity-60'>{t('common.modelProvider.selectModel')}</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
hasRemoved && (
|
||||
<Tooltip
|
||||
selector='model-selector-remove-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.selector.tip')}</div>
|
||||
}
|
||||
>
|
||||
<AlertCircle className='mr-1 w-4 h-4 text-[#F04438]' />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{
|
||||
!readonly && !whenEmptyGoToSetting && (
|
||||
<ChevronDown className={`w-4 h-4 text-gray-700 ${open ? 'opacity-100' : 'opacity-60'}`} />
|
||||
)
|
||||
}
|
||||
{
|
||||
whenEmptyGoToSetting && (value && value.modelName && value.providerName) && (
|
||||
<ChevronDown className={`w-4 h-4 text-gray-700 ${open ? 'opacity-100' : 'opacity-60'}`} />
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</Popover.Button>
|
||||
{!readonly && (
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave='transition ease-in duration-100'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
>
|
||||
<Popover.Panel className={cn(popClassName, isShowModelModeType ? 'max-w-[312px]' : 'max-w-[260px]', 'absolute top-10 p-1 min-w-[232px] max-h-[366px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg overflow-auto z-10')}>
|
||||
<div className='px-2 pt-2 pb-1'>
|
||||
<div className='flex items-center px-2 h-8 bg-gray-100 rounded-lg'>
|
||||
<div className='mr-1.5 p-[1px]'><SearchLg className='w-[14px] h-[14px] text-gray-400' /></div>
|
||||
<div className='grow px-0.5'>
|
||||
<input
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
className={`
|
||||
block w-full h-8 bg-transparent text-[13px] text-gray-700
|
||||
outline-none appearance-none border-none
|
||||
`}
|
||||
placeholder={t('common.modelProvider.searchModel') || ''}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
search && (
|
||||
<div className='ml-1 p-0.5 cursor-pointer' onClick={() => setSearch('')}>
|
||||
<XCircle className='w-3 h-3 text-gray-400' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
modelOptions.map((model) => {
|
||||
if (model.type === 'provider') {
|
||||
return (
|
||||
<div
|
||||
className='px-3 pt-2 pb-1 text-xs font-medium text-gray-500'
|
||||
key={`${model.type}-${model.value}`}
|
||||
>
|
||||
<ProviderName provideName={model.value} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (model.type === 'model') {
|
||||
return (
|
||||
<Popover.Button
|
||||
key={`${model.providerName}-${model.value}`}
|
||||
className={`${s.optionItem}
|
||||
flex items-center px-3 w-full h-8 rounded-lg hover:bg-gray-50
|
||||
${!readonly ? 'cursor-pointer' : 'cursor-auto'}
|
||||
${(value?.providerName === model.providerName && value?.modelName === model.value) && 'bg-gray-50'}
|
||||
`}
|
||||
onClick={() => {
|
||||
const selectedModel = modelList.find((item) => {
|
||||
return item.model_name === model.value && item.model_provider.provider_name === model.providerName
|
||||
})
|
||||
onChange(selectedModel as BackendModel)
|
||||
}}
|
||||
>
|
||||
<ModelIcon
|
||||
className='mr-2 shrink-0'
|
||||
modelId={model.value}
|
||||
providerName={model.providerName}
|
||||
/>
|
||||
<div className='mr-2 grow flex items-center text-left text-sm text-gray-900 truncate'>
|
||||
<ModelName modelId={model.value} modelDisplayName={model.modelDisplayName} />
|
||||
{isShowModelModeType && (
|
||||
<ModelModeTypeLabel className={`${s.modelModeLabel} ml-2`} type={model.model_mode} />
|
||||
)}
|
||||
</div>
|
||||
{ (value?.providerName === model.providerName && value?.modelName === model.value) && <Check className='shrink-0 w-4 h-4 text-primary-600' /> }
|
||||
</Popover.Button>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
{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>
|
||||
)}
|
||||
|
||||
{isShowAddModel && (
|
||||
<div
|
||||
className='border-t flex items-center h-9 pl-3 text-xs text-[#155EEF] cursor-pointer'
|
||||
style={{
|
||||
borderColor: 'rgba(0, 0, 0, 0.05)',
|
||||
}}
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
>
|
||||
<CubeOutline className='w-4 h-4 mr-2' />
|
||||
<div>{t('common.model.addMoreModel')}</div>
|
||||
</div>
|
||||
)}
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
)}
|
||||
</Popover>
|
||||
<ModelModal
|
||||
isShow={showRerankModal}
|
||||
modelModal={cohereConfig.modal}
|
||||
onCancel={() => setShowRerankModal(false)}
|
||||
onSave={handleRerankModalSave}
|
||||
mode={'add'}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelSelector
|
||||
|
|
@ -1,358 +0,0 @@
|
|||
import type { FC } from 'react'
|
||||
import React, { Fragment, useEffect, useRef, useState } from 'react'
|
||||
import useSWR from 'swr'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import _ from 'lodash-es'
|
||||
import cn from 'classnames'
|
||||
import ModelModal from '../model-modal'
|
||||
import cohereConfig from '../configs/cohere'
|
||||
import s from './style.module.css'
|
||||
import type { BackendModel, FormValue, ProviderEnum } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ModelType } from '@/app/components/header/account-setting/model-page/declarations'
|
||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { Check, LinkExternal01, SearchLg } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { XCircle } from '@/app/components/base/icons/src/vender/solid/general'
|
||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
|
||||
import ModelName from '@/app/components/app/configuration/config-model/model-name'
|
||||
import ProviderName from '@/app/components/app/configuration/config-model/provider-name'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import ModelModeTypeLabel from '@/app/components/app/configuration/config-model/model-mode-type-label'
|
||||
import type { ModelModeType } from '@/types/app'
|
||||
import { CubeOutline } from '@/app/components/base/icons/src/vender/line/shapes'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { fetchDefaultModal, setModelProvider } from '@/service/common'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
type Props = {
|
||||
value: {
|
||||
providerName: ProviderEnum
|
||||
modelName: string
|
||||
} | undefined
|
||||
modelType: ModelType
|
||||
isShowModelModeType?: boolean
|
||||
isShowAddModel?: boolean
|
||||
supportAgentThought?: boolean
|
||||
onChange: (value: BackendModel) => void
|
||||
popClassName?: string
|
||||
readonly?: boolean
|
||||
triggerIconSmall?: boolean
|
||||
whenEmptyGoToSetting?: boolean
|
||||
onUpdate?: () => void
|
||||
widthSameToTrigger?: boolean
|
||||
}
|
||||
|
||||
type ModelOption = {
|
||||
type: 'model'
|
||||
value: string
|
||||
providerName: ProviderEnum
|
||||
modelDisplayName: string
|
||||
model_mode: ModelModeType
|
||||
} | {
|
||||
type: 'provider'
|
||||
value: ProviderEnum
|
||||
}
|
||||
|
||||
const ModelSelector: FC<Props> = ({
|
||||
value,
|
||||
modelType,
|
||||
isShowModelModeType,
|
||||
isShowAddModel,
|
||||
supportAgentThought,
|
||||
onChange,
|
||||
popClassName,
|
||||
readonly,
|
||||
triggerIconSmall,
|
||||
whenEmptyGoToSetting,
|
||||
onUpdate,
|
||||
widthSameToTrigger,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { setShowAccountSettingModal } = useModalContext()
|
||||
const {
|
||||
textGenerationModelList,
|
||||
embeddingsModelList,
|
||||
speech2textModelList,
|
||||
rerankModelList,
|
||||
agentThoughtModelList,
|
||||
updateModelList,
|
||||
} = useProviderContext()
|
||||
const [search, setSearch] = useState('')
|
||||
const modelList = supportAgentThought
|
||||
? agentThoughtModelList
|
||||
: ({
|
||||
[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 = (() => {
|
||||
if (!search)
|
||||
return {}
|
||||
|
||||
const res: Record<string, string> = {}
|
||||
modelList.forEach(({ model_name, model_display_name }) => {
|
||||
res[model_name] = model_display_name
|
||||
})
|
||||
return res
|
||||
})()
|
||||
const filteredModelList = search
|
||||
? modelList.filter(({ model_name }) => {
|
||||
if (allModelNames[model_name].includes(search))
|
||||
return true
|
||||
|
||||
return false
|
||||
})
|
||||
: modelList
|
||||
|
||||
const hasRemoved = (value && value.modelName && value.providerName) && !modelList.find(({ model_name, model_provider }) => model_name === value.modelName && model_provider.provider_name === value.providerName)
|
||||
|
||||
const modelOptions: ModelOption[] = (() => {
|
||||
const providers = _.uniq(filteredModelList.map(item => item.model_provider.provider_name))
|
||||
const res: ModelOption[] = []
|
||||
providers.forEach((providerName) => {
|
||||
res.push({
|
||||
type: 'provider',
|
||||
value: providerName,
|
||||
})
|
||||
const models = filteredModelList.filter(m => m.model_provider.provider_name === providerName)
|
||||
models.forEach(({ model_name, model_display_name, model_mode }) => {
|
||||
res.push({
|
||||
type: 'model',
|
||||
providerName,
|
||||
value: model_name,
|
||||
modelDisplayName: model_display_name,
|
||||
model_mode,
|
||||
})
|
||||
})
|
||||
})
|
||||
return res
|
||||
})()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [showRerankModal, setShowRerankModal] = useState(false)
|
||||
const [shouldFetchRerankDefaultModel, setShouldFetchRerankDefaultModel] = useState(false)
|
||||
const { notify } = useToastContext()
|
||||
const { data: rerankDefaultModel } = useSWR(shouldFetchRerankDefaultModel ? '/workspaces/current/default-model?model_type=reranking' : null, fetchDefaultModal)
|
||||
const handleOpenRerankModal = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
e.stopPropagation()
|
||||
setShowRerankModal(true)
|
||||
}
|
||||
const handleRerankModalSave = async (originValue?: FormValue) => {
|
||||
if (originValue) {
|
||||
try {
|
||||
eventEmitter?.emit('provider-save')
|
||||
const res = await setModelProvider({
|
||||
url: `/workspaces/current/model-providers/${cohereConfig.modal.key}`,
|
||||
body: {
|
||||
config: originValue,
|
||||
},
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
updateModelList(ModelType.reranking)
|
||||
setShowRerankModal(false)
|
||||
setShouldFetchRerankDefaultModel(true)
|
||||
if (onUpdate)
|
||||
onUpdate()
|
||||
}
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
catch (e) {
|
||||
eventEmitter?.emit('')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
const triggerRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (rerankDefaultModel && whenEmptyGoToSetting)
|
||||
onChange(rerankDefaultModel)
|
||||
}, [rerankDefaultModel])
|
||||
|
||||
return (
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='bottom-start'
|
||||
offset={4}
|
||||
>
|
||||
<div className='relative'>
|
||||
<PortalToFollowElemTrigger onClick={() => setOpen(v => !v)} 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]')}>
|
||||
{
|
||||
<div ref={triggerRef} className='flex items-center w-full cursor-pointer'>
|
||||
{
|
||||
(value && value.modelName && value.providerName)
|
||||
? (
|
||||
<>
|
||||
<ModelIcon
|
||||
className={cn('mr-1.5', !triggerIconSmall && 'w-5 h-5')}
|
||||
modelId={value.modelName}
|
||||
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 || value.modelName} />
|
||||
{isShowModelModeType && (
|
||||
<ModelModeTypeLabel className='ml-2' type={currModel?.model_mode as ModelModeType} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
: whenEmptyGoToSetting
|
||||
? (
|
||||
<div className='grow flex items-center h-9 justify-between' onClick={handleOpenRerankModal}>
|
||||
<div className='flex items-center text-[13px] font-medium text-primary-500'>
|
||||
<CubeOutline className='mr-1.5 w-4 h-4' />
|
||||
{t('common.modelProvider.selector.rerankTip')}
|
||||
</div>
|
||||
<LinkExternal01 className='w-3 h-3 text-gray-500' />
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div className='grow text-left text-sm text-gray-800 opacity-60'>{t('common.modelProvider.selectModel')}</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
hasRemoved && (
|
||||
<Tooltip
|
||||
selector='model-selector-remove-tip'
|
||||
htmlContent={
|
||||
<div className='w-[261px] text-gray-500'>{t('common.modelProvider.selector.tip')}</div>
|
||||
}
|
||||
>
|
||||
<AlertCircle className='mr-1 w-4 h-4 text-[#F04438]' />
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
{
|
||||
!readonly && !whenEmptyGoToSetting && (
|
||||
<ChevronDown className={`w-4 h-4 text-gray-700 ${open ? 'opacity-100' : 'opacity-60'}`} />
|
||||
)
|
||||
}
|
||||
{
|
||||
whenEmptyGoToSetting && (value && value.modelName && value.providerName) && (
|
||||
<ChevronDown className={`w-4 h-4 text-gray-700 ${open ? 'opacity-100' : 'opacity-60'}`} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
</PortalToFollowElemTrigger>
|
||||
{!readonly && (
|
||||
<PortalToFollowElemContent
|
||||
className={cn(popClassName, !widthSameToTrigger && (isShowModelModeType ? 'max-w-[312px]' : 'max-w-[260px]'), 'absolute top-10 p-1 min-w-[232px] max-h-[366px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg overflow-auto z-[999]')}
|
||||
style={{
|
||||
width: (widthSameToTrigger && triggerRef.current?.offsetWidth) ? `${triggerRef.current?.offsetWidth}px` : 'auto',
|
||||
}}
|
||||
>
|
||||
<div className='px-2 pt-2 pb-1'>
|
||||
<div className='flex items-center px-2 h-8 bg-gray-100 rounded-lg'>
|
||||
<div className='mr-1.5 p-[1px]'><SearchLg className='w-[14px] h-[14px] text-gray-400' /></div>
|
||||
<div className='grow px-0.5'>
|
||||
<input
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
className={`
|
||||
block w-full h-8 bg-transparent text-[13px] text-gray-700
|
||||
outline-none appearance-none border-none
|
||||
`}
|
||||
placeholder={t('common.modelProvider.searchModel') || ''}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
search && (
|
||||
<div className='ml-1 p-0.5 cursor-pointer' onClick={() => setSearch('')}>
|
||||
<XCircle className='w-3 h-3 text-gray-400' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
modelOptions.map((model) => {
|
||||
if (model.type === 'provider') {
|
||||
return (
|
||||
<div
|
||||
className='px-3 pt-2 pb-1 text-xs font-medium text-gray-500'
|
||||
key={`${model.type}-${model.value}`}
|
||||
>
|
||||
<ProviderName provideName={model.value} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (model.type === 'model') {
|
||||
return (
|
||||
<div
|
||||
key={`${model.providerName}-${model.value}`}
|
||||
className={`${s.optionItem}
|
||||
flex items-center px-3 w-full h-8 rounded-lg hover:bg-gray-50
|
||||
${!readonly ? 'cursor-pointer' : 'cursor-auto'}
|
||||
${(value?.providerName === model.providerName && value?.modelName === model.value) && 'bg-gray-50'}
|
||||
`}
|
||||
onClick={() => {
|
||||
const selectedModel = modelList.find((item) => {
|
||||
return item.model_name === model.value && item.model_provider.provider_name === model.providerName
|
||||
})
|
||||
onChange(selectedModel as BackendModel)
|
||||
setOpen(false)
|
||||
}}
|
||||
>
|
||||
<ModelIcon
|
||||
className='mr-2 shrink-0'
|
||||
modelId={model.value}
|
||||
providerName={model.providerName}
|
||||
/>
|
||||
<div className='mr-2 grow flex items-center text-left text-sm text-gray-900 truncate'>
|
||||
<ModelName modelId={model.value} modelDisplayName={model.modelDisplayName} />
|
||||
{isShowModelModeType && (
|
||||
<ModelModeTypeLabel className={`${s.modelModeLabel} ml-2`} type={model.model_mode} />
|
||||
)}
|
||||
</div>
|
||||
{(value?.providerName === model.providerName && value?.modelName === model.value) && <Check className='shrink-0 w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
})
|
||||
}
|
||||
{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>
|
||||
)}
|
||||
|
||||
{isShowAddModel && (
|
||||
<div
|
||||
className='border-t flex items-center h-9 pl-3 text-xs text-[#155EEF] cursor-pointer'
|
||||
style={{
|
||||
borderColor: 'rgba(0, 0, 0, 0.05)',
|
||||
}}
|
||||
onClick={() => setShowAccountSettingModal({ payload: 'provider' })}
|
||||
>
|
||||
<CubeOutline className='w-4 h-4 mr-2' />
|
||||
<div>{t('common.model.addMoreModel')}</div>
|
||||
</div>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
)}
|
||||
</div>
|
||||
<ModelModal
|
||||
isShow={showRerankModal}
|
||||
modelModal={cohereConfig.modal}
|
||||
onCancel={() => setShowRerankModal(false)}
|
||||
onSave={handleRerankModalSave}
|
||||
mode={'add'}
|
||||
/>
|
||||
</PortalToFollowElem>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelSelector
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
.modelModeLabel {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.optionItem:hover .modelModeLabel {
|
||||
visibility: visible;
|
||||
}
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
import { Fragment } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Check, DotsHorizontal, Trash03 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
|
||||
const itemClassName = `
|
||||
flex items-center px-3 h-9 text-sm text-gray-700 rounded-lg cursor-pointer
|
||||
`
|
||||
|
||||
type SelectorProps = {
|
||||
value?: string
|
||||
onOperate: (v: Record<string, string>) => void
|
||||
hiddenOptions?: boolean
|
||||
className?: (v: boolean) => string
|
||||
deleteText?: string
|
||||
}
|
||||
const Selector: FC<SelectorProps> = ({
|
||||
value,
|
||||
onOperate,
|
||||
hiddenOptions,
|
||||
className,
|
||||
deleteText,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const options = [
|
||||
{
|
||||
key: 'custom',
|
||||
text: 'API',
|
||||
},
|
||||
{
|
||||
key: 'system',
|
||||
text: t('common.modelProvider.quota'),
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<Popover className='relative'>
|
||||
<Popover.Button>
|
||||
{
|
||||
({ open }) => (
|
||||
<div className={`
|
||||
flex justify-center items-center w-6 h-6 rounded-md hover:bg-gray-50 cursor-pointer
|
||||
${open && 'bg-gray-50'}
|
||||
${className && className(open)}
|
||||
`}>
|
||||
<DotsHorizontal className='w-3 h-3 text-gray-700' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Popover.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave='transition ease-in duration-100'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'
|
||||
>
|
||||
<Popover.Panel className='absolute top-7 right-0 w-[192px] bg-white border-[0.5px] border-gray-200 rounded-lg shadow-lg z-10'>
|
||||
{
|
||||
!hiddenOptions && (
|
||||
<>
|
||||
<div className='p-1'>
|
||||
<div className='px-3 pt-2 pb-1 text-sm font-medium text-gray-700'>{t('common.modelProvider.card.priorityUse')}</div>
|
||||
{
|
||||
options.map(option => (
|
||||
<Popover.Button as={Fragment} key={option.key}>
|
||||
<div
|
||||
className={`${itemClassName} hover:bg-gray-50`}
|
||||
onClick={() => onOperate({ type: 'priority', value: option.key })}>
|
||||
<div className='grow'>{option.text}</div>
|
||||
{value === option.key && <Check className='w-4 h-4 text-primary-600' />}
|
||||
</div>
|
||||
</Popover.Button>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className='h-[1px] bg-gray-100' />
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className='p-1'>
|
||||
<Popover.Button as={Fragment}>
|
||||
<div
|
||||
className={`group ${itemClassName} hover:bg-[#FEF3F2] hover:text-[#D92D20]`}
|
||||
onClick={() => onOperate({ type: 'delete' })}>
|
||||
<Trash03 className='mr-2 w-4 h-4 text-gray-500 group-hover:text-[#D92D20]' />
|
||||
{deleteText || t('common.modelProvider.card.removeKey')}
|
||||
</div>
|
||||
</Popover.Button>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
</Transition>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
export default Selector
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { ValidatedStatus } from '../key-validator/declarations'
|
||||
import { ProviderEnum } from './declarations'
|
||||
import { validateModelProvider } from '@/service/common'
|
||||
|
||||
export const ConfigurableProviders = [ProviderEnum.azure_openai, ProviderEnum.replicate, ProviderEnum.huggingface_hub, ProviderEnum.xinference, ProviderEnum.openllm, ProviderEnum.localai]
|
||||
|
||||
export const validateModelProviderFn = async (providerName: ProviderEnum, v: any) => {
|
||||
let body, url
|
||||
|
||||
if (ConfigurableProviders.includes(providerName)) {
|
||||
const { model_name, model_type, ...config } = v
|
||||
body = {
|
||||
model_name,
|
||||
model_type,
|
||||
config,
|
||||
}
|
||||
url = `/workspaces/current/model-providers/${providerName}/models/validate`
|
||||
}
|
||||
else {
|
||||
body = {
|
||||
config: v,
|
||||
}
|
||||
url = `/workspaces/current/model-providers/${providerName}/validate`
|
||||
}
|
||||
try {
|
||||
const res = await validateModelProvider({ url, body })
|
||||
if (res.result === 'success')
|
||||
return Promise.resolve({ status: ValidatedStatus.Success })
|
||||
else
|
||||
return Promise.resolve({ status: ValidatedStatus.Error, message: res.error })
|
||||
}
|
||||
catch (e: any) {
|
||||
return Promise.resolve({ status: ValidatedStatus.Error, message: e.message })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,216 @@
|
|||
export type FormValue = Record<string, any>
|
||||
|
||||
export type TypeWithI18N<T = string> = {
|
||||
'en_US': T
|
||||
'zh_Hans': T
|
||||
}
|
||||
|
||||
export enum FormTypeEnum {
|
||||
textInput = 'text-input',
|
||||
secretInput = 'secret-input',
|
||||
select = 'select',
|
||||
radio = 'radio',
|
||||
}
|
||||
|
||||
export type FormOption = {
|
||||
label: TypeWithI18N
|
||||
value: string
|
||||
show_on: FormShowOnObject[]
|
||||
}
|
||||
|
||||
export enum ModelTypeEnum {
|
||||
textGeneration = 'llm',
|
||||
textEmbedding = 'text-embedding',
|
||||
rerank = 'rerank',
|
||||
speech2text = 'speech2text',
|
||||
moderation = 'moderation',
|
||||
}
|
||||
|
||||
export const MODEL_TYPE_TEXT = {
|
||||
[ModelTypeEnum.textGeneration]: 'LLM',
|
||||
[ModelTypeEnum.textEmbedding]: 'Text Embedding',
|
||||
[ModelTypeEnum.rerank]: 'Rerank',
|
||||
[ModelTypeEnum.speech2text]: 'Speech2text',
|
||||
[ModelTypeEnum.moderation]: 'Moderation',
|
||||
}
|
||||
|
||||
export enum ConfigurateMethodEnum {
|
||||
predefinedModel = 'predefined-model',
|
||||
customizableModel = 'customizable-model',
|
||||
fetchFromRemote = 'fetch-from-remote',
|
||||
}
|
||||
|
||||
export enum ModelFeatureEnum {
|
||||
toolCall = 'tool-call',
|
||||
multiToolCall = 'multi-tool-call',
|
||||
agentThought = 'agent-thought',
|
||||
vision = 'vision',
|
||||
}
|
||||
|
||||
export enum ModelFeatureTextEnum {
|
||||
toolCall = 'Tool Call',
|
||||
multiToolCall = 'Multi Tool Call',
|
||||
agentThought = 'Agent Thought',
|
||||
vision = 'Vision',
|
||||
}
|
||||
|
||||
export enum ModelStatusEnum {
|
||||
active = 'active',
|
||||
noConfigure = 'no-configure',
|
||||
quotaExceeded = 'quota-exceeded',
|
||||
noPermission = 'no-permission',
|
||||
}
|
||||
|
||||
export const MODEL_STATUS_TEXT = {
|
||||
[ModelStatusEnum.noConfigure]: {
|
||||
en_US: 'No Configure',
|
||||
zh_Hans: '未配置凭据',
|
||||
},
|
||||
[ModelStatusEnum.quotaExceeded]: {
|
||||
en_US: 'Quota Exceeded',
|
||||
zh_Hans: '额度不足',
|
||||
},
|
||||
[ModelStatusEnum.noPermission]: {
|
||||
en_US: 'No Permission',
|
||||
zh_Hans: '无使用权限',
|
||||
},
|
||||
}
|
||||
|
||||
export enum CustomConfigurationStatusEnum {
|
||||
active = 'active',
|
||||
noConfigure = 'no-configure',
|
||||
}
|
||||
|
||||
export type FormShowOnObject = {
|
||||
variable: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export type CredentialFormSchemaBase = {
|
||||
variable: string
|
||||
label: TypeWithI18N
|
||||
type: FormTypeEnum
|
||||
required: boolean
|
||||
default?: string
|
||||
show_on: FormShowOnObject[]
|
||||
}
|
||||
|
||||
export type CredentialFormSchemaTextInput = CredentialFormSchemaBase & { max_length?: number; placeholder?: TypeWithI18N }
|
||||
export type CredentialFormSchemaSelect = CredentialFormSchemaBase & { options: FormOption[]; placeholder?: TypeWithI18N }
|
||||
export type CredentialFormSchemaRadio = CredentialFormSchemaBase & { options: FormOption[] }
|
||||
export type CredentialFormSchemaSecretInput = CredentialFormSchemaBase & { placeholder?: TypeWithI18N }
|
||||
export type CredentialFormSchema = CredentialFormSchemaTextInput | CredentialFormSchemaSelect | CredentialFormSchemaRadio | CredentialFormSchemaSecretInput
|
||||
|
||||
export type ModelItem = {
|
||||
model: string
|
||||
label: TypeWithI18N
|
||||
model_type: ModelTypeEnum
|
||||
features?: ModelFeatureEnum[]
|
||||
fetch_from: ConfigurateMethodEnum
|
||||
status: ModelStatusEnum
|
||||
model_properties: Record<string, string | number>
|
||||
deprecated?: boolean
|
||||
}
|
||||
|
||||
export enum PreferredProviderTypeEnum {
|
||||
system = 'system',
|
||||
custom = 'custom',
|
||||
}
|
||||
|
||||
export enum CurrentSystemQuotaTypeEnum {
|
||||
trial = 'trial',
|
||||
free = 'free',
|
||||
paid = 'paid',
|
||||
}
|
||||
|
||||
export enum QuotaUnitEnum {
|
||||
times = 'times',
|
||||
tokens = 'tokens',
|
||||
}
|
||||
|
||||
export type QuotaConfiguration = {
|
||||
quota_type: CurrentSystemQuotaTypeEnum
|
||||
quota_unit: QuotaUnitEnum
|
||||
quota_limit: number
|
||||
quota_used: number
|
||||
last_used: number
|
||||
is_valid: boolean
|
||||
}
|
||||
|
||||
export type ModelProvider = {
|
||||
provider: string
|
||||
label: TypeWithI18N
|
||||
description?: TypeWithI18N
|
||||
help: {
|
||||
title: TypeWithI18N
|
||||
url: TypeWithI18N
|
||||
}
|
||||
icon_small: TypeWithI18N
|
||||
icon_large: TypeWithI18N
|
||||
background?: string
|
||||
supported_model_types: ModelTypeEnum[]
|
||||
configurate_methods: ConfigurateMethodEnum[]
|
||||
provider_credential_schema: {
|
||||
credential_form_schemas: CredentialFormSchema[]
|
||||
}
|
||||
model_credential_schema: {
|
||||
model: {
|
||||
label: TypeWithI18N
|
||||
placeholder: TypeWithI18N
|
||||
}
|
||||
credential_form_schemas: CredentialFormSchema[]
|
||||
}
|
||||
preferred_provider_type: PreferredProviderTypeEnum
|
||||
custom_configuration: {
|
||||
status: CustomConfigurationStatusEnum
|
||||
}
|
||||
system_configuration: {
|
||||
enabled: boolean
|
||||
current_quota_type: CurrentSystemQuotaTypeEnum
|
||||
quota_configurations: QuotaConfiguration[]
|
||||
}
|
||||
}
|
||||
|
||||
export type Model = {
|
||||
provider: string
|
||||
icon_large: TypeWithI18N
|
||||
icon_small: TypeWithI18N
|
||||
label: TypeWithI18N
|
||||
models: ModelItem[]
|
||||
status: ModelStatusEnum
|
||||
}
|
||||
|
||||
export type DefaultModelResponse = {
|
||||
model: string
|
||||
model_type: ModelTypeEnum
|
||||
provider: {
|
||||
provider: string
|
||||
icon_large: TypeWithI18N
|
||||
icon_small: TypeWithI18N
|
||||
}
|
||||
}
|
||||
|
||||
export type DefaultModel = {
|
||||
provider: string
|
||||
model: string
|
||||
}
|
||||
|
||||
export type CustomConfigrationModelFixedFields = {
|
||||
__model_name: string
|
||||
__model_type: ModelTypeEnum
|
||||
}
|
||||
|
||||
export type ModelParameterRule = {
|
||||
default?: number | string | boolean | string[]
|
||||
help?: TypeWithI18N
|
||||
label: TypeWithI18N
|
||||
min?: number
|
||||
max?: number
|
||||
name: string
|
||||
precision?: number
|
||||
required: false
|
||||
type: string
|
||||
use_template?: string
|
||||
options?: string[]
|
||||
tagPlaceholder?: TypeWithI18N
|
||||
}
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useSWR, { useSWRConfig } from 'swr'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import type {
|
||||
CustomConfigrationModelFixedFields,
|
||||
DefaultModel,
|
||||
DefaultModelResponse,
|
||||
Model,
|
||||
} from './declarations'
|
||||
import {
|
||||
ConfigurateMethodEnum,
|
||||
ModelTypeEnum,
|
||||
} from './declarations'
|
||||
import { languageMaps } from './utils'
|
||||
import I18n from '@/context/i18n'
|
||||
import {
|
||||
fetchDefaultModal,
|
||||
fetchModelList,
|
||||
fetchModelProviderCredentials,
|
||||
fetchModelProviders,
|
||||
getPayUrl,
|
||||
submitFreeQuota,
|
||||
} from '@/service/common'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
type UseDefaultModelAndModelList = (
|
||||
defaultModel: DefaultModelResponse | undefined,
|
||||
modelList: Model[],
|
||||
) => [DefaultModel | undefined, (model: DefaultModel) => void]
|
||||
export const useSystemDefaultModelAndModelList: UseDefaultModelAndModelList = (
|
||||
defaultModel,
|
||||
modelList,
|
||||
) => {
|
||||
const currentDefaultModel = useMemo(() => {
|
||||
const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider.provider)
|
||||
const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model)
|
||||
const currentDefaultModel = currentProvider && currentModel && {
|
||||
model: currentModel.model,
|
||||
provider: currentProvider.provider,
|
||||
}
|
||||
|
||||
return currentDefaultModel
|
||||
}, [defaultModel, modelList])
|
||||
const [defaultModelState, setDefaultModelState] = useState<DefaultModel | undefined>(currentDefaultModel)
|
||||
const handleDefaultModelChange = useCallback((model: DefaultModel) => {
|
||||
setDefaultModelState(model)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
setDefaultModelState(currentDefaultModel)
|
||||
}, [currentDefaultModel])
|
||||
|
||||
return [defaultModelState, handleDefaultModelChange]
|
||||
}
|
||||
|
||||
export const useLanguage = () => {
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
return languageMaps[locale]
|
||||
}
|
||||
|
||||
export const useProviderCrenditialsFormSchemasValue = (
|
||||
provider: string,
|
||||
configurateMethod: ConfigurateMethodEnum,
|
||||
configured?: boolean,
|
||||
currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields,
|
||||
) => {
|
||||
const { data: predefinedFormSchemasValue } = useSWR(
|
||||
(configurateMethod === ConfigurateMethodEnum.predefinedModel && configured)
|
||||
? `/workspaces/current/model-providers/${provider}/credentials`
|
||||
: null,
|
||||
fetchModelProviderCredentials,
|
||||
)
|
||||
const { data: customFormSchemasValue } = useSWR(
|
||||
(configurateMethod === ConfigurateMethodEnum.customizableModel && currentCustomConfigrationModelFixedFields)
|
||||
? `/workspaces/current/model-providers/${provider}/models/credentials?model=${currentCustomConfigrationModelFixedFields?.__model_name}&model_type=${currentCustomConfigrationModelFixedFields?.__model_type}`
|
||||
: null,
|
||||
fetchModelProviderCredentials,
|
||||
)
|
||||
|
||||
const value = useMemo(() => {
|
||||
return configurateMethod === ConfigurateMethodEnum.predefinedModel
|
||||
? predefinedFormSchemasValue?.credentials
|
||||
: customFormSchemasValue?.credentials
|
||||
? {
|
||||
...customFormSchemasValue?.credentials,
|
||||
...currentCustomConfigrationModelFixedFields,
|
||||
}
|
||||
: undefined
|
||||
}, [
|
||||
configurateMethod,
|
||||
currentCustomConfigrationModelFixedFields,
|
||||
customFormSchemasValue?.credentials,
|
||||
predefinedFormSchemasValue?.credentials,
|
||||
])
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
export type ModelTypeIndex = 1 | 2 | 3 | 4
|
||||
export const MODEL_TYPE_MAPS = {
|
||||
1: ModelTypeEnum.textGeneration,
|
||||
2: ModelTypeEnum.textEmbedding,
|
||||
3: ModelTypeEnum.rerank,
|
||||
4: ModelTypeEnum.speech2text,
|
||||
}
|
||||
|
||||
export const useModelList = (type: ModelTypeIndex) => {
|
||||
const { data, mutate, isLoading } = useSWR(`/workspaces/current/models/model-types/${MODEL_TYPE_MAPS[type]}`, fetchModelList)
|
||||
|
||||
return {
|
||||
data: data?.data || [],
|
||||
mutate,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useDefaultModel = (type: ModelTypeIndex) => {
|
||||
const { data, mutate, isLoading } = useSWR(`/workspaces/current/default-model?model_type=${MODEL_TYPE_MAPS[type]}`, fetchDefaultModal)
|
||||
|
||||
return {
|
||||
data: data?.data,
|
||||
mutate,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useCurrentProviderAndModel = (modelList: Model[], defaultModel?: DefaultModel) => {
|
||||
const currentProvider = modelList.find(provider => provider.provider === defaultModel?.provider)
|
||||
const currentModel = currentProvider?.models.find(model => model.model === defaultModel?.model)
|
||||
|
||||
return {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
}
|
||||
}
|
||||
|
||||
export const useTextGenerationCurrentProviderAndModelAndModelList = (defaultModel?: DefaultModel) => {
|
||||
const { textGenerationModelList } = useProviderContext()
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
} = useCurrentProviderAndModel(textGenerationModelList, defaultModel)
|
||||
|
||||
return {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
textGenerationModelList,
|
||||
}
|
||||
}
|
||||
|
||||
export const useAgentThoughtCurrentProviderAndModelAndModelList = (defaultModel?: DefaultModel) => {
|
||||
const { agentThoughtModelList } = useProviderContext()
|
||||
const {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
} = useCurrentProviderAndModel(agentThoughtModelList, defaultModel)
|
||||
|
||||
return {
|
||||
currentProvider,
|
||||
currentModel,
|
||||
agentThoughtModelList,
|
||||
}
|
||||
}
|
||||
|
||||
export const useModelListAndDefaultModel = (type: ModelTypeIndex) => {
|
||||
const { data: modelList } = useModelList(type)
|
||||
const { data: defaultModel } = useDefaultModel(type)
|
||||
|
||||
return {
|
||||
modelList,
|
||||
defaultModel,
|
||||
}
|
||||
}
|
||||
|
||||
export const useModelListAndDefaultModelAndCurrentProviderAndModel = (type: ModelTypeIndex) => {
|
||||
const { modelList, defaultModel } = useModelListAndDefaultModel(type)
|
||||
const { currentProvider, currentModel } = useCurrentProviderAndModel(
|
||||
modelList,
|
||||
{ provider: defaultModel?.provider.provider || '', model: defaultModel?.model || '' },
|
||||
)
|
||||
|
||||
return {
|
||||
modelList,
|
||||
defaultModel,
|
||||
currentProvider,
|
||||
currentModel,
|
||||
}
|
||||
}
|
||||
|
||||
export const useUpdateModelList = () => {
|
||||
const { mutate } = useSWRConfig()
|
||||
|
||||
const updateModelList = useCallback((type: ModelTypeIndex | ModelTypeEnum) => {
|
||||
const modelType = typeof type === 'number' ? MODEL_TYPE_MAPS[type] : type
|
||||
mutate(`/workspaces/current/models/model-types/${modelType}`)
|
||||
}, [mutate])
|
||||
|
||||
return updateModelList
|
||||
}
|
||||
|
||||
export const useAnthropicBuyQuota = () => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleGetPayUrl = async () => {
|
||||
if (loading)
|
||||
return
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
const res = await getPayUrl('/workspaces/current/model-providers/anthropic/checkout-url')
|
||||
|
||||
window.location.href = res.url
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return handleGetPayUrl
|
||||
}
|
||||
|
||||
export const useFreeQuota = (onSuccess: () => void) => {
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleClick = async (type: string) => {
|
||||
if (loading)
|
||||
return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const res = await submitFreeQuota(`/workspaces/current/model-providers/${type}/free-quota-submit`)
|
||||
|
||||
if (res.type === 'redirect' && res.redirect_url)
|
||||
window.location.href = res.redirect_url
|
||||
else if (res.type === 'submit' && res.result === 'success')
|
||||
onSuccess()
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
return handleClick
|
||||
}
|
||||
|
||||
export const useModelProviders = () => {
|
||||
const { data: providersData, mutate, isLoading } = useSWR('/workspaces/current/model-providers', fetchModelProviders)
|
||||
|
||||
return {
|
||||
data: providersData?.data || [],
|
||||
mutate,
|
||||
isLoading,
|
||||
}
|
||||
}
|
||||
|
||||
export const useUpdateModelProvidersAndModelList = () => {
|
||||
const { mutate } = useSWRConfig()
|
||||
const updateModelList = useUpdateModelList()
|
||||
|
||||
const updateModelProvidersAndModelList = useCallback(() => {
|
||||
mutate('/workspaces/current/model-providers')
|
||||
updateModelList(1)
|
||||
}, [mutate, updateModelList])
|
||||
|
||||
return updateModelProvidersAndModelList
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
import { useMemo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SystemModelSelector from './system-model-selector'
|
||||
import ProviderAddedCard, { UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST } from './provider-added-card'
|
||||
import ProviderCard from './provider-card'
|
||||
import type {
|
||||
ConfigurateMethodEnum,
|
||||
CustomConfigrationModelFixedFields,
|
||||
ModelProvider,
|
||||
} from './declarations'
|
||||
import { CustomConfigurationStatusEnum } from './declarations'
|
||||
import {
|
||||
useDefaultModel,
|
||||
useUpdateModelProvidersAndModelList,
|
||||
} from './hooks'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
|
||||
const ModelProviderPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const updateModelProvidersAndModelList = useUpdateModelProvidersAndModelList()
|
||||
const { data: textGenerationDefaultModel } = useDefaultModel(1)
|
||||
const { data: embeddingsDefaultModel } = useDefaultModel(2)
|
||||
const { data: rerankDefaultModel } = useDefaultModel(3)
|
||||
const { data: speech2textDefaultModel } = useDefaultModel(4)
|
||||
const { modelProviders: providers } = useProviderContext()
|
||||
const { setShowModelModal } = useModalContext()
|
||||
const defaultModelNotConfigured = !textGenerationDefaultModel && !embeddingsDefaultModel && !speech2textDefaultModel && !rerankDefaultModel
|
||||
const [configedProviders, notConfigedProviders] = useMemo(() => {
|
||||
const configedProviders: ModelProvider[] = []
|
||||
const notConfigedProviders: ModelProvider[] = []
|
||||
|
||||
providers.forEach((provider) => {
|
||||
if (provider.custom_configuration.status === CustomConfigurationStatusEnum.active || provider.system_configuration.enabled === true)
|
||||
configedProviders.push(provider)
|
||||
else
|
||||
notConfigedProviders.push(provider)
|
||||
})
|
||||
|
||||
return [configedProviders, notConfigedProviders]
|
||||
}, [providers])
|
||||
|
||||
const handleOpenModal = (
|
||||
provider: ModelProvider,
|
||||
configurateMethod: ConfigurateMethodEnum,
|
||||
customConfigrationModelFixedFields?: CustomConfigrationModelFixedFields,
|
||||
) => {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurateMethod: configurateMethod,
|
||||
currentCustomConfigrationModelFixedFields: customConfigrationModelFixedFields,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
updateModelProvidersAndModelList()
|
||||
|
||||
if (customConfigrationModelFixedFields && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) {
|
||||
eventEmitter?.emit({
|
||||
type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST,
|
||||
payload: provider.provider,
|
||||
} as any)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative pt-1 -mt-2'>
|
||||
<div className={`flex items-center justify-between mb-2 h-8 ${defaultModelNotConfigured && 'px-3 bg-[#FFFAEB] rounded-lg border border-[#FEF0C7]'}`}>
|
||||
{
|
||||
defaultModelNotConfigured
|
||||
? (
|
||||
<div className='flex items-center text-xs font-medium text-gray-700'>
|
||||
<AlertTriangle className='mr-1 w-3 h-3 text-[#F79009]' />
|
||||
{t('common.modelProvider.notConfigured')}
|
||||
</div>
|
||||
)
|
||||
: <div className='text-sm font-medium text-gray-800'>{t('common.modelProvider.models')}</div>
|
||||
}
|
||||
<SystemModelSelector
|
||||
textGenerationDefaultModel={textGenerationDefaultModel}
|
||||
embeddingsDefaultModel={embeddingsDefaultModel}
|
||||
rerankDefaultModel={rerankDefaultModel}
|
||||
speech2textDefaultModel={speech2textDefaultModel}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
!!configedProviders?.length && (
|
||||
<div className='pb-3'>
|
||||
{
|
||||
configedProviders?.map(provider => (
|
||||
<ProviderAddedCard
|
||||
key={provider.provider}
|
||||
provider={provider}
|
||||
onOpenModal={(configurateMethod: ConfigurateMethodEnum, currentCustomConfigrationModelFixedFields?: CustomConfigrationModelFixedFields) => handleOpenModal(provider, configurateMethod, currentCustomConfigrationModelFixedFields)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!!notConfigedProviders?.length && (
|
||||
<>
|
||||
<div className='flex items-center mb-2 text-xs font-semibold text-gray-500'>
|
||||
+ {t('common.modelProvider.addMoreModelProvider')}
|
||||
<span className='grow ml-3 h-[1px] bg-gradient-to-r from-[#f3f4f6]' />
|
||||
</div>
|
||||
<div className='grid grid-cols-3 gap-2'>
|
||||
{
|
||||
notConfigedProviders?.map(provider => (
|
||||
<ProviderCard
|
||||
key={provider.provider}
|
||||
provider={provider}
|
||||
onOpenModal={(configurateMethod: ConfigurateMethodEnum) => handleOpenModal(provider, configurateMethod)}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelProviderPage
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import type { FC, ReactNode } from 'react'
|
||||
|
||||
type ModelBadgeProps = {
|
||||
className?: string
|
||||
children?: ReactNode
|
||||
}
|
||||
const ModelBadge: FC<ModelBadgeProps> = ({
|
||||
className,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className={`
|
||||
flex items-center px-1 h-[18px] rounded-[5px] border border-black/[0.08] bg-white/[0.48]
|
||||
text-[10px] font-medium text-gray-500
|
||||
${className}
|
||||
`}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModelBadge
|
||||