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>
This commit is contained in:
takatost 2024-01-02 23:42:00 +08:00 • committed by GitHub
commit d069c668f8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
807 changed files with 171310 additions and 23806 deletions

View 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

View file

@ -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 = {

View file

@ -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}>

View file

@ -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)

View file

@ -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

View file

@ -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>

View file

@ -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'>

View file

@ -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 = ({

View file

@ -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
}

View file

@ -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}
/>

View file

@ -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,
})
}}
/>

View file

@ -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[] = [
{

View file

@ -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'

View file

@ -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'