'use client' import type { FC } from 'react' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import useSWR from 'swr' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' import { usePathname } from 'next/navigation' import produce from 'immer' import { useBoolean, useGetState } from 'ahooks' import { clone, isEqual } from 'lodash-es' import { CodeBracketIcon } from '@heroicons/react/20/solid' import { useShallow } from 'zustand/react/shallow' import AgentSettingButton from '@/app/components/app/configuration/config/agent-setting-button' import useAdvancedPromptConfig from '@/app/components/app/configuration/hooks/use-advanced-prompt-config' import EditHistoryModal from '@/app/components/app/configuration/config-prompt/conversation-history/edit-modal' import { useDebugWithSingleOrMultipleModel, useFormattingChangedDispatcher, } from '@/app/components/app/configuration/debug/hooks' import type { ModelAndParameter } from '@/app/components/app/configuration/debug/types' import Button from '@/app/components/base/button' import Loading from '@/app/components/base/loading' import AppPublisher from '@/app/components/app/app-publisher/features-wrapper' import type { AnnotationReplyConfig, DatasetConfigs, Inputs, ModelConfig, ModerationConfig, MoreLikeThisConfig, PromptConfig, PromptVariable, TextToSpeechConfig, } from '@/models/debug' 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 Config from '@/app/components/app/configuration/config' import Debug from '@/app/components/app/configuration/debug' import Confirm from '@/app/components/base/confirm' import { ModelFeatureEnum, ModelTypeEnum } 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' import { fetchDatasets } from '@/service/datasets' import { useProviderContext } from '@/context/provider-context' import { AgentStrategy, AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app' import { PromptMode } from '@/models/debug' import { ANNOTATION_DEFAULT, DATASET_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config' import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset' 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 { useModelListAndDefaultModelAndCurrentProviderAndModel, useTextGenerationCurrentProviderAndModelAndModelList, } from '@/app/components/header/account-setting/model-provider-page/hooks' import { fetchCollectionList } from '@/service/tools' import { type Collection } from '@/app/components/tools/types' import { useStore as useAppStore } from '@/app/components/app/store' import { getMultipleRetrievalConfig, getSelectedDatasetsMode, } from '@/app/components/workflow/nodes/knowledge-retrieval/utils' import { FeaturesProvider } from '@/app/components/base/features' import type { Features as FeaturesData, FileUpload } from '@/app/components/base/features/types' import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants' import { SupportUploadFileTypes } from '@/app/components/workflow/types' import NewFeaturePanel from '@/app/components/base/features/new-feature-panel' import { fetchFileUploadConfig } from '@/service/common' type PublishConfig = { modelConfig: ModelConfig completionParams: FormValue } const Configuration: FC = () => { const { t } = useTranslation() const { notify } = useContext(ToastContext) const { appDetail, showAppConfigureFeaturesModal, setAppSiderbarExpand, setShowAppConfigureFeaturesModal } = useAppStore(useShallow(state => ({ appDetail: state.appDetail, setAppSiderbarExpand: state.setAppSiderbarExpand, showAppConfigureFeaturesModal: state.showAppConfigureFeaturesModal, setShowAppConfigureFeaturesModal: state.setShowAppConfigureFeaturesModal, }))) const { data: fileUploadConfigResponse } = useSWR({ url: '/files/upload' }, fetchFileUploadConfig) const latestPublishedAt = useMemo(() => appDetail?.model_config.updated_at, [appDetail]) const [formattingChanged, setFormattingChanged] = useState(false) const { setShowAccountSettingModal } = useModalContext() const [hasFetchedDetail, setHasFetchedDetail] = useState(false) const isLoading = !hasFetchedDetail const pathname = usePathname() const matched = pathname.match(/\/app\/([^/]+)/) const appId = (matched?.length && matched[1]) ? matched[1] : '' const [mode, setMode] = useState('') const [publishedConfig, setPublishedConfig] = useState(null) const [conversationId, setConversationId] = useState('') const media = useBreakpoints() const isMobile = media === MediaType.mobile const [isShowDebugPanel, { setTrue: showDebugPanel, setFalse: hideDebugPanel }] = useBoolean(false) const [introduction, setIntroduction] = useState('') const [suggestedQuestions, setSuggestedQuestions] = useState([]) const [controlClearChatMessage, setControlClearChatMessage] = useState(0) const [prevPromptConfig, setPrevPromptConfig] = useState({ prompt_template: '', prompt_variables: [], }) const [moreLikeThisConfig, setMoreLikeThisConfig] = useState({ enabled: false, }) const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState({ enabled: false, }) const [speechToTextConfig, setSpeechToTextConfig] = useState({ enabled: false, }) const [textToSpeechConfig, setTextToSpeechConfig] = useState({ enabled: false, voice: '', language: '', }) const [citationConfig, setCitationConfig] = useState({ enabled: false, }) const [annotationConfig, doSetAnnotationConfig] = useState({ id: '', enabled: false, score_threshold: ANNOTATION_DEFAULT.score_threshold, embedding_model: { embedding_provider_name: '', embedding_model_name: '', }, }) const formattingChangedDispatcher = useFormattingChangedDispatcher() const setAnnotationConfig = (config: AnnotationReplyConfig, notSetFormatChanged?: boolean) => { doSetAnnotationConfig(config) if (!notSetFormatChanged) formattingChangedDispatcher() } const [moderationConfig, setModerationConfig] = useState({ enabled: false, }) const [externalDataToolsConfig, setExternalDataToolsConfig] = useState([]) const [inputs, setInputs] = useState({}) const [query, setQuery] = useState('') const [completionParams, doSetCompletionParams] = useState({}) const [_, setTempStop, getTempStop] = useGetState([]) const setCompletionParams = (value: FormValue) => { const params = { ...value } // eslint-disable-next-line @typescript-eslint/no-use-before-define if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) { params.stop = getTempStop() setTempStop([]) } doSetCompletionParams(params) } const [modelConfig, doSetModelConfig] = useState({ provider: 'openai', model_id: 'gpt-3.5-turbo', mode: ModelModeType.unset, configs: { prompt_template: '', prompt_variables: [] as PromptVariable[], }, more_like_this: null, opening_statement: '', suggested_questions: [], sensitive_word_avoidance: null, speech_to_text: null, text_to_speech: null, file_upload: null, suggested_questions_after_answer: null, retriever_resource: null, annotation_reply: null, dataSets: [], agentConfig: DEFAULT_AGENT_SETTING, }) const isAgent = mode === 'agent-chat' const isOpenAI = modelConfig.provider === 'openai' const [collectionList, setCollectionList] = useState([]) useEffect(() => { }, []) const [datasetConfigs, setDatasetConfigs] = useState({ retrieval_model: RETRIEVE_TYPE.multiWay, reranking_model: { reranking_provider_name: '', reranking_model_name: '', }, top_k: DATASET_DEFAULT.top_k, score_threshold_enabled: false, score_threshold: DATASET_DEFAULT.score_threshold, datasets: { datasets: [], }, }) const setModelConfig = (newModelConfig: ModelConfig) => { doSetModelConfig(newModelConfig) } const modelModeType = modelConfig.mode const modeModeTypeRef = useRef(modelModeType) useEffect(() => { modeModeTypeRef.current = modelModeType }, [modelModeType]) const [dataSets, setDataSets] = useState([]) const contextVar = modelConfig.configs.prompt_variables.find((item: any) => item.is_context_var)?.key const hasSetContextVar = !!contextVar const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false) const selectedIds = dataSets.map(item => item.id) const [rerankSettingModalOpen, setRerankSettingModalOpen] = useState(false) const { currentModel: currentRerankModel, currentProvider: currentRerankProvider, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) const handleSelect = (data: DataSet[]) => { if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) { hideSelectDataSet() return } formattingChangedDispatcher() let newDatasets = data if (data.find(item => !item.name)) { // has not loaded selected dataset const newSelected = produce(data, (draft: any) => { data.forEach((item, index) => { if (!item.name) { // not fetched database const newItem = dataSets.find(i => i.id === item.id) if (newItem) draft[index] = newItem } }) }) setDataSets(newSelected) newDatasets = newSelected } else { setDataSets(data) } hideSelectDataSet() const { allExternal, allInternal, mixtureInternalAndExternal, mixtureHighQualityAndEconomic, inconsistentEmbeddingModel, } = getSelectedDatasetsMode(newDatasets) if ( (allInternal && (mixtureHighQualityAndEconomic || inconsistentEmbeddingModel)) || mixtureInternalAndExternal || allExternal ) setRerankSettingModalOpen(true) const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = datasetConfigs const retrievalConfig = getMultipleRetrievalConfig({ top_k: restConfigs.top_k, score_threshold: restConfigs.score_threshold, reranking_model: restConfigs.reranking_model && { provider: restConfigs.reranking_model.reranking_provider_name, model: restConfigs.reranking_model.reranking_model_name, }, reranking_mode: restConfigs.reranking_mode, weights: restConfigs.weights, reranking_enable: restConfigs.reranking_enable, }, newDatasets, dataSets, { provider: currentRerankProvider?.provider, model: currentRerankModel?.model, }) setDatasetConfigs({ ...retrievalConfig, reranking_model: { reranking_provider_name: retrievalConfig?.reranking_model?.provider || '', reranking_model_name: retrievalConfig?.reranking_model?.model || '', }, retrieval_model, score_threshold_enabled, datasets, }) } const [isShowHistoryModal, { setTrue: showHistoryModal, setFalse: hideHistoryModal }] = useBoolean(false) const syncToPublishedConfig = (_publishedConfig: PublishConfig) => { const modelConfig = _publishedConfig.modelConfig setModelConfig(_publishedConfig.modelConfig) setCompletionParams(_publishedConfig.completionParams) setDataSets(modelConfig.dataSets || []) // reset feature setIntroduction(modelConfig.opening_statement!) setMoreLikeThisConfig(modelConfig.more_like_this || { enabled: false, }) setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer || { enabled: false, }) setSpeechToTextConfig(modelConfig.speech_to_text || { enabled: false, }) setTextToSpeechConfig(modelConfig.text_to_speech || { enabled: false, voice: '', language: '', }) setCitationConfig(modelConfig.retriever_resource || { enabled: false, }) } const { isAPIKeySet } = useProviderContext() const { currentModel: currModel, textGenerationModelList, } = useTextGenerationCurrentProviderAndModelAndModelList( { provider: modelConfig.provider, model: modelConfig.model_id, }, ) const isFunctionCall = (() => { const features = currModel?.features if (!features) return false return features.includes(ModelFeatureEnum.toolCall) || features.includes(ModelFeatureEnum.multiToolCall) })() // Fill old app data missing model mode. useEffect(() => { if (hasFetchedDetail && !modelModeType) { const mode = currModel?.model_properties.mode as (ModelModeType | undefined) if (mode) { const newModelConfig = produce(modelConfig, (draft: ModelConfig) => { draft.mode = mode }) setModelConfig(newModelConfig) } } }, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel, modelConfig]) const [promptMode, doSetPromptMode] = useState(PromptMode.simple) const isAdvancedMode = promptMode === PromptMode.advanced const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true) const setPromptMode = async (mode: PromptMode) => { if (mode === PromptMode.advanced) { // eslint-disable-next-line @typescript-eslint/no-use-before-define await migrateToDefaultPrompt() setCanReturnToSimpleMode(true) } doSetPromptMode(mode) } const [visionConfig, doSetVisionConfig] = useState({ enabled: false, number_limits: 2, detail: Resolution.low, transfer_methods: [TransferMethod.local_file], }) const handleSetVisionConfig = (config: VisionSettings, notNoticeFormattingChanged?: boolean) => { doSetVisionConfig({ enabled: config.enabled || false, number_limits: config.number_limits || 2, detail: config.detail || Resolution.low, transfer_methods: config.transfer_methods || [TransferMethod.local_file], }) if (!notNoticeFormattingChanged) formattingChangedDispatcher() } const { chatPromptConfig, setChatPromptConfig, completionPromptConfig, setCompletionPromptConfig, currentAdvancedPrompt, setCurrentAdvancedPrompt, hasSetBlockStatus, setConversationHistoriesRole, migrateToDefaultPrompt, } = useAdvancedPromptConfig({ appMode: mode, modelName: modelConfig.model_id, promptMode, modelModeType, prePrompt: modelConfig.configs.prompt_template, hasSetDataSet: dataSets.length > 0, onUserChangedPrompt: () => { setCanReturnToSimpleMode(false) }, completionParams, setCompletionParams, setStop: setTempStop, }) const setModel = async ({ modelId, provider, mode: modeMode, features, }: { modelId: string; provider: string; mode: string; features: string[] }) => { if (isAdvancedMode) { const appMode = mode if (modeMode === ModelModeType.completion) { if (appMode !== AppType.completion) { if (!completionPromptConfig.prompt?.text || !completionPromptConfig.conversation_histories_role.assistant_prefix || !completionPromptConfig.conversation_histories_role.user_prefix) await migrateToDefaultPrompt(true, ModelModeType.completion) } else { if (!completionPromptConfig.prompt?.text) await migrateToDefaultPrompt(true, ModelModeType.completion) } } if (modeMode === ModelModeType.chat) { if (chatPromptConfig.prompt.length === 0) await migrateToDefaultPrompt(true, ModelModeType.chat) } } const newModelConfig = produce(modelConfig, (draft: ModelConfig) => { draft.provider = provider draft.model_id = modelId draft.mode = modeMode as ModelModeType }) setModelConfig(newModelConfig) const supportVision = features && features.includes(ModelFeatureEnum.vision) handleSetVisionConfig({ ...visionConfig, enabled: supportVision, }, true) setCompletionParams({}) } const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision) const isShowDocumentConfig = !!currModel?.features?.includes(ModelFeatureEnum.document) const isAllowVideoUpload = !!currModel?.features?.includes(ModelFeatureEnum.video) // *** web app features *** const featuresData: FeaturesData = useMemo(() => { return { moreLikeThis: modelConfig.more_like_this || { enabled: false }, opening: { enabled: !!modelConfig.opening_statement, opening_statement: modelConfig.opening_statement || '', suggested_questions: modelConfig.suggested_questions || [], }, moderation: modelConfig.sensitive_word_avoidance || { enabled: false }, speech2text: modelConfig.speech_to_text || { enabled: false }, text2speech: modelConfig.text_to_speech || { enabled: false }, file: { image: { detail: modelConfig.file_upload?.image?.detail || Resolution.high, enabled: !!modelConfig.file_upload?.image?.enabled, number_limits: modelConfig.file_upload?.image?.number_limits || 3, transfer_methods: modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], }, enabled: !!(modelConfig.file_upload?.enabled || modelConfig.file_upload?.image?.enabled), allowed_file_types: modelConfig.file_upload?.allowed_file_types || [], allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...FILE_EXTS[SupportUploadFileTypes.image], ...FILE_EXTS[SupportUploadFileTypes.video]].map(ext => `.${ext}`), allowed_file_upload_methods: modelConfig.file_upload?.allowed_file_upload_methods || modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'], number_limits: modelConfig.file_upload?.number_limits || modelConfig.file_upload?.image?.number_limits || 3, fileUploadConfig: fileUploadConfigResponse, } as FileUpload, suggested: modelConfig.suggested_questions_after_answer || { enabled: false }, citation: modelConfig.retriever_resource || { enabled: false }, annotationReply: modelConfig.annotation_reply || { enabled: false }, } }, [fileUploadConfigResponse, modelConfig]) const handleFeaturesChange = useCallback((flag: any) => { setShowAppConfigureFeaturesModal(true) if (flag) formattingChangedDispatcher() }, [formattingChangedDispatcher, setShowAppConfigureFeaturesModal]) const handleAddPromptVariable = useCallback((variable: PromptVariable[]) => { const newModelConfig = produce(modelConfig, (draft: ModelConfig) => { draft.configs.prompt_variables = variable }) setModelConfig(newModelConfig) }, [modelConfig]) useEffect(() => { (async () => { const collectionList = await fetchCollectionList() setCollectionList(collectionList) fetchAppDetail({ url: '/apps', id: appId }).then(async (res: any) => { setMode(res.mode) const modelConfig = res.model_config const promptMode = modelConfig.prompt_type === PromptMode.advanced ? PromptMode.advanced : PromptMode.simple doSetPromptMode(promptMode) if (promptMode === PromptMode.advanced) { if (modelConfig.chat_prompt_config && modelConfig.chat_prompt_config.prompt.length > 0) setChatPromptConfig(modelConfig.chat_prompt_config) else setChatPromptConfig(clone(DEFAULT_CHAT_PROMPT_CONFIG) as any) setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any) setCanReturnToSimpleMode(false) } const model = res.model_config.model let datasets: any = null // old dataset struct if (modelConfig.agent_mode?.tools?.find(({ dataset }: any) => dataset?.enabled)) datasets = modelConfig.agent_mode?.tools.filter(({ dataset }: any) => dataset?.enabled) // new dataset struct else if (modelConfig.dataset_configs.datasets?.datasets?.length > 0) datasets = modelConfig.dataset_configs?.datasets?.datasets if (dataSets && datasets?.length && datasets?.length > 0) { const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasets.map(({ dataset }: any) => dataset.id) } }) datasets = dataSetsWithDetail setDataSets(datasets) } setIntroduction(modelConfig.opening_statement) setSuggestedQuestions(modelConfig.suggested_questions || []) if (modelConfig.more_like_this) setMoreLikeThisConfig(modelConfig.more_like_this) if (modelConfig.suggested_questions_after_answer) setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer) if (modelConfig.speech_to_text) setSpeechToTextConfig(modelConfig.speech_to_text) if (modelConfig.text_to_speech) setTextToSpeechConfig(modelConfig.text_to_speech) if (modelConfig.retriever_resource) setCitationConfig(modelConfig.retriever_resource) if (modelConfig.annotation_reply) setAnnotationConfig(modelConfig.annotation_reply, true) if (modelConfig.sensitive_word_avoidance) setModerationConfig(modelConfig.sensitive_word_avoidance) if (modelConfig.external_data_tools) setExternalDataToolsConfig(modelConfig.external_data_tools) const config = { modelConfig: { provider: model.provider, model_id: model.name, mode: model.mode, configs: { prompt_template: modelConfig.pre_prompt || '', prompt_variables: userInputsFormToPromptVariables( [ ...modelConfig.user_input_form, ...( modelConfig.external_data_tools?.length ? modelConfig.external_data_tools.map((item: any) => { return { external_data_tool: { variable: item.variable as string, label: item.label as string, enabled: item.enabled, type: item.type as string, config: item.config, required: true, icon: item.icon, icon_background: item.icon_background, }, } }) : [] ), ], modelConfig.dataset_query_variable, ), }, more_like_this: modelConfig.more_like_this, opening_statement: modelConfig.opening_statement, suggested_questions: modelConfig.suggested_questions, sensitive_word_avoidance: modelConfig.sensitive_word_avoidance, speech_to_text: modelConfig.speech_to_text, text_to_speech: modelConfig.text_to_speech, file_upload: modelConfig.file_upload, suggested_questions_after_answer: modelConfig.suggested_questions_after_answer, retriever_resource: modelConfig.retriever_resource, annotation_reply: modelConfig.annotation_reply, external_data_tools: modelConfig.external_data_tools, dataSets: datasets || [], // eslint-disable-next-line multiline-ternary agentConfig: res.mode === 'agent-chat' ? { max_iteration: DEFAULT_AGENT_SETTING.max_iteration, ...modelConfig.agent_mode, // remove dataset enabled: true, // modelConfig.agent_mode?.enabled is not correct. old app: the value of app with dataset's is always true tools: modelConfig.agent_mode?.tools.filter((tool: any) => { return !tool.dataset }).map((tool: any) => { return { ...tool, isDeleted: res.deleted_tools?.includes(tool.tool_name), notAuthor: collectionList.find(c => tool.provider_id === c.id)?.is_team_authorization === false, } }), } : DEFAULT_AGENT_SETTING, }, completionParams: model.completion_params, } if (modelConfig.file_upload) handleSetVisionConfig(modelConfig.file_upload.image, true) syncToPublishedConfig(config) setPublishedConfig(config) const retrievalConfig = getMultipleRetrievalConfig(modelConfig.dataset_configs, datasets, datasets, { provider: currentRerankProvider?.provider, model: currentRerankModel?.model, }) setDatasetConfigs({ retrieval_model: RETRIEVE_TYPE.multiWay, ...modelConfig.dataset_configs, ...retrievalConfig, }) setHasFetchedDetail(true) }) })() // eslint-disable-next-line react-hooks/exhaustive-deps }, [appId]) const promptEmpty = (() => { if (mode !== AppType.completion) return false if (isAdvancedMode) { if (modelModeType === ModelModeType.chat) return chatPromptConfig.prompt.every(({ text }: any) => !text) else return !completionPromptConfig.prompt?.text } else { return !modelConfig.configs.prompt_template } })() const cannotPublish = (() => { if (mode !== AppType.completion) { if (!isAdvancedMode) return false if (modelModeType === ModelModeType.completion) { if (!hasSetBlockStatus.history || !hasSetBlockStatus.query) return true return false } return false } else { return promptEmpty } })() const contextVarEmpty = mode === AppType.completion && dataSets.length > 0 && !hasSetContextVar const onPublish = async (modelAndParameter?: ModelAndParameter, features?: FeaturesData) => { const modelId = modelAndParameter?.model || modelConfig.model_id const promptTemplate = modelConfig.configs.prompt_template const promptVariables = modelConfig.configs.prompt_variables if (promptEmpty) { notify({ type: 'error', message: t('appDebug.otherError.promptNoBeEmpty') }) return } if (isAdvancedMode && mode !== AppType.completion) { if (modelModeType === ModelModeType.completion) { if (!hasSetBlockStatus.history) { notify({ type: 'error', message: t('appDebug.otherError.historyNoBeEmpty') }) return } if (!hasSetBlockStatus.query) { notify({ type: 'error', message: t('appDebug.otherError.queryNoBeEmpty') }) return } } } if (contextVarEmpty) { notify({ type: 'error', message: t('appDebug.feature.dataSet.queryVariable.contextVarNotEmpty') }) return } const postDatasets = dataSets.map(({ id }) => ({ dataset: { enabled: true, id, }, })) const fileUpload = { ...features?.file } delete fileUpload?.fileUploadConfig // new model config data struct const data: BackendModelConfig = { // Simple Mode prompt pre_prompt: !isAdvancedMode ? promptTemplate : '', prompt_type: promptMode, chat_prompt_config: {}, completion_prompt_config: {}, user_input_form: promptVariablesToUserInputsForm(promptVariables), dataset_query_variable: contextVar || '', // features more_like_this: features?.moreLikeThis as any, opening_statement: features?.opening?.enabled ? (features.opening?.opening_statement || '') : '', suggested_questions: features?.opening?.enabled ? (features.opening?.suggested_questions || []) : [], sensitive_word_avoidance: features?.moderation as any, speech_to_text: features?.speech2text as any, text_to_speech: features?.text2speech as any, file_upload: fileUpload as any, suggested_questions_after_answer: features?.suggested as any, retriever_resource: features?.citation as any, agent_mode: { ...modelConfig.agentConfig, strategy: isFunctionCall ? AgentStrategy.functionCall : AgentStrategy.react, }, model: { provider: modelAndParameter?.provider || modelConfig.provider, name: modelId, mode: modelConfig.mode, completion_params: modelAndParameter?.parameters || completionParams as any, }, dataset_configs: { ...datasetConfigs, datasets: { datasets: [...postDatasets], } as any, }, } if (isAdvancedMode) { data.chat_prompt_config = chatPromptConfig data.completion_prompt_config = completionPromptConfig } await updateAppModelConfig({ url: `/apps/${appId}/model-config`, body: data }) const newModelConfig = produce(modelConfig, (draft: any) => { draft.opening_statement = introduction draft.more_like_this = moreLikeThisConfig draft.suggested_questions_after_answer = suggestedQuestionsAfterAnswerConfig draft.speech_to_text = speechToTextConfig draft.text_to_speech = textToSpeechConfig draft.retriever_resource = citationConfig draft.dataSets = dataSets }) setPublishedConfig({ modelConfig: newModelConfig, completionParams, }) notify({ type: 'success', message: t('common.api.success') }) setCanReturnToSimpleMode(false) return true } const [showUseGPT4Confirm, setShowUseGPT4Confirm] = useState(false) const { debugWithMultipleModel, multipleModelConfigs, handleMultipleModelConfigsChange, } = useDebugWithSingleOrMultipleModel(appId) const handleDebugWithMultipleModelChange = () => { handleMultipleModelConfigsChange( true, [ { id: `${Date.now()}`, model: modelConfig.model_id, provider: modelConfig.provider, parameters: completionParams }, { id: `${Date.now()}-no-repeat`, model: '', provider: '', parameters: {} }, ], ) setAppSiderbarExpand('collapse') } if (isLoading) { return
} return ( <>
{/* Header */}
{t('appDebug.orchestrate')}
{isAdvancedMode && (
{t('appDebug.promptMode.advanced')}
)}
{/* Agent Setting */} {isAgent && ( { const nextConfig = produce(modelConfig, (draft: ModelConfig) => { draft.agentConfig = config }) setModelConfig(nextConfig) }} /> )} {/* Model and Parameters */} {!debugWithMultipleModel && ( <> { setCompletionParams(newParams) }} debugWithMultipleModel={debugWithMultipleModel} onDebugWithMultipleModelChange={handleDebugWithMultipleModelChange} />
)} {isMobile && ( )} syncToPublishedConfig(publishedConfig!), }} />
{!isMobile &&
setShowAccountSettingModal({ payload: 'provider' })} inputs={inputs} modelParameterParams={{ setModel: setModel as any, onCompletionParamsChange: setCompletionParams, }} debugWithMultipleModel={debugWithMultipleModel} multipleModelConfigs={multipleModelConfigs} onMultipleModelConfigsChange={handleMultipleModelConfigsChange} />
}
{showUseGPT4Confirm && ( { setShowAccountSettingModal({ payload: 'provider' }) setShowUseGPT4Confirm(false) }} onCancel={() => setShowUseGPT4Confirm(false)} /> )} {isShowSelectDataSet && ( )} {isShowHistoryModal && ( { setConversationHistoriesRole(data) hideHistoryModal() }} /> )} {isMobile && ( setShowAccountSettingModal({ payload: 'provider' })} inputs={inputs} modelParameterParams={{ setModel: setModel as any, onCompletionParamsChange: setCompletionParams, }} debugWithMultipleModel={debugWithMultipleModel} multipleModelConfigs={multipleModelConfigs} onMultipleModelConfigsChange={handleMultipleModelConfigsChange} /> )} {showAppConfigureFeaturesModal && ( setShowAppConfigureFeaturesModal(false)} promptVariables={modelConfig.configs.prompt_variables} onAutoAddPromptVariable={handleAddPromptVariable} /> )}
) } export default React.memo(Configuration)