index.tsx 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. 'use client'
  2. import type { FC } from 'react'
  3. import React, { useEffect, useRef, useState } from 'react'
  4. import { useTranslation } from 'react-i18next'
  5. import { useContext } from 'use-context-selector'
  6. import { usePathname } from 'next/navigation'
  7. import produce from 'immer'
  8. import { useBoolean, useGetState } from 'ahooks'
  9. import cn from 'classnames'
  10. import { clone, isEqual } from 'lodash-es'
  11. import { CodeBracketIcon } from '@heroicons/react/20/solid'
  12. import Button from '../../base/button'
  13. import Loading from '../../base/loading'
  14. import s from './style.module.css'
  15. import useAdvancedPromptConfig from './hooks/use-advanced-prompt-config'
  16. import EditHistoryModal from './config-prompt/conversation-histroy/edit-modal'
  17. import type {
  18. AnnotationReplyConfig,
  19. DatasetConfigs,
  20. Inputs,
  21. ModelConfig,
  22. ModerationConfig,
  23. MoreLikeThisConfig,
  24. PromptConfig,
  25. PromptVariable,
  26. } from '@/models/debug'
  27. import type { ExternalDataTool } from '@/models/common'
  28. import type { DataSet } from '@/models/datasets'
  29. import type { ModelConfig as BackendModelConfig, VisionSettings } from '@/types/app'
  30. import ConfigContext from '@/context/debug-configuration'
  31. // import ConfigModel from '@/app/components/app/configuration/config-model'
  32. import Config from '@/app/components/app/configuration/config'
  33. import Debug from '@/app/components/app/configuration/debug'
  34. import Confirm from '@/app/components/base/confirm'
  35. import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
  36. import { ToastContext } from '@/app/components/base/toast'
  37. import { fetchAppDetail, updateAppModelConfig } from '@/service/apps'
  38. import { promptVariablesToUserInputsForm, userInputsFormToPromptVariables } from '@/utils/model-config'
  39. import { fetchDatasets } from '@/service/datasets'
  40. import { useProviderContext } from '@/context/provider-context'
  41. import { AppType, ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
  42. import { FlipBackward } from '@/app/components/base/icons/src/vender/line/arrows'
  43. import { PromptMode } from '@/models/debug'
  44. import { ANNOTATION_DEFAULT, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
  45. import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
  46. import I18n from '@/context/i18n'
  47. import { useModalContext } from '@/context/modal-context'
  48. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  49. import Drawer from '@/app/components/base/drawer'
  50. import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
  51. import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
  52. import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
  53. type PublichConfig = {
  54. modelConfig: ModelConfig
  55. completionParams: FormValue
  56. }
  57. const Configuration: FC = () => {
  58. const { t } = useTranslation()
  59. const { notify } = useContext(ToastContext)
  60. const [formattingChanged, setFormattingChanged] = useState(false)
  61. const { setShowAccountSettingModal } = useModalContext()
  62. const [hasFetchedDetail, setHasFetchedDetail] = useState(false)
  63. const isLoading = !hasFetchedDetail
  64. const pathname = usePathname()
  65. const matched = pathname.match(/\/app\/([^/]+)/)
  66. const appId = (matched?.length && matched[1]) ? matched[1] : ''
  67. const [mode, setMode] = useState('')
  68. const [publishedConfig, setPublishedConfig] = useState<PublichConfig | null>(null)
  69. const [conversationId, setConversationId] = useState<string | null>('')
  70. const media = useBreakpoints()
  71. const isMobile = media === MediaType.mobile
  72. const [isShowDebugPanel, { setTrue: showDebugPanel, setFalse: hideDebugPanel }] = useBoolean(false)
  73. const [introduction, setIntroduction] = useState<string>('')
  74. const [controlClearChatMessage, setControlClearChatMessage] = useState(0)
  75. const [prevPromptConfig, setPrevPromptConfig] = useState<PromptConfig>({
  76. prompt_template: '',
  77. prompt_variables: [],
  78. })
  79. const [moreLikeThisConfig, setMoreLikeThisConfig] = useState<MoreLikeThisConfig>({
  80. enabled: false,
  81. })
  82. const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<MoreLikeThisConfig>({
  83. enabled: false,
  84. })
  85. const [speechToTextConfig, setSpeechToTextConfig] = useState<MoreLikeThisConfig>({
  86. enabled: false,
  87. })
  88. const [citationConfig, setCitationConfig] = useState<MoreLikeThisConfig>({
  89. enabled: false,
  90. })
  91. const [annotationConfig, doSetAnnotationConfig] = useState<AnnotationReplyConfig>({
  92. id: '',
  93. enabled: false,
  94. score_threshold: ANNOTATION_DEFAULT.score_threshold,
  95. embedding_model: {
  96. embedding_provider_name: '',
  97. embedding_model_name: '',
  98. },
  99. })
  100. const setAnnotationConfig = (config: AnnotationReplyConfig, notSetFormatChanged?: boolean) => {
  101. doSetAnnotationConfig(config)
  102. if (!notSetFormatChanged)
  103. setFormattingChanged(true)
  104. }
  105. const [moderationConfig, setModerationConfig] = useState<ModerationConfig>({
  106. enabled: false,
  107. })
  108. const [externalDataToolsConfig, setExternalDataToolsConfig] = useState<ExternalDataTool[]>([])
  109. const [inputs, setInputs] = useState<Inputs>({})
  110. const [query, setQuery] = useState('')
  111. const [completionParams, doSetCompletionParams] = useState<FormValue>({})
  112. const [tempStop, setTempStop, getTempStop] = useGetState<string[]>([])
  113. const setCompletionParams = (value: FormValue) => {
  114. const params = { ...value }
  115. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  116. if ((!params.stop || params.stop.length === 0) && (modeModeTypeRef.current === ModelModeType.completion)) {
  117. params.stop = getTempStop()
  118. setTempStop([])
  119. }
  120. doSetCompletionParams(params)
  121. }
  122. const [modelConfig, doSetModelConfig] = useState<ModelConfig>({
  123. provider: 'openai',
  124. model_id: 'gpt-3.5-turbo',
  125. mode: ModelModeType.unset,
  126. configs: {
  127. prompt_template: '',
  128. prompt_variables: [] as PromptVariable[],
  129. },
  130. opening_statement: '',
  131. more_like_this: null,
  132. suggested_questions_after_answer: null,
  133. speech_to_text: null,
  134. retriever_resource: null,
  135. sensitive_word_avoidance: null,
  136. dataSets: [],
  137. })
  138. const [datasetConfigs, setDatasetConfigs] = useState<DatasetConfigs>({
  139. retrieval_model: RETRIEVE_TYPE.oneWay,
  140. reranking_model: {
  141. reranking_provider_name: '',
  142. reranking_model_name: '',
  143. },
  144. top_k: 2,
  145. score_threshold_enabled: false,
  146. score_threshold: 0.7,
  147. })
  148. const setModelConfig = (newModelConfig: ModelConfig) => {
  149. doSetModelConfig(newModelConfig)
  150. }
  151. const modelModeType = modelConfig.mode
  152. const modeModeTypeRef = useRef(modelModeType)
  153. useEffect(() => {
  154. modeModeTypeRef.current = modelModeType
  155. }, [modelModeType])
  156. const [dataSets, setDataSets] = useState<DataSet[]>([])
  157. const contextVar = modelConfig.configs.prompt_variables.find(item => item.is_context_var)?.key
  158. const hasSetContextVar = !!contextVar
  159. const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
  160. const selectedIds = dataSets.map(item => item.id)
  161. const handleSelect = (data: DataSet[]) => {
  162. if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
  163. hideSelectDataSet()
  164. return
  165. }
  166. setFormattingChanged(true)
  167. if (data.find(item => !item.name)) { // has not loaded selected dataset
  168. const newSelected = produce(data, (draft: any) => {
  169. data.forEach((item, index) => {
  170. if (!item.name) { // not fetched database
  171. const newItem = dataSets.find(i => i.id === item.id)
  172. if (newItem)
  173. draft[index] = newItem
  174. }
  175. })
  176. })
  177. setDataSets(newSelected)
  178. }
  179. else {
  180. setDataSets(data)
  181. }
  182. hideSelectDataSet()
  183. }
  184. const [isShowHistoryModal, { setTrue: showHistoryModal, setFalse: hideHistoryModal }] = useBoolean(false)
  185. const syncToPublishedConfig = (_publishedConfig: PublichConfig) => {
  186. const modelConfig = _publishedConfig.modelConfig
  187. setModelConfig(_publishedConfig.modelConfig)
  188. setCompletionParams(_publishedConfig.completionParams)
  189. setDataSets(modelConfig.dataSets || [])
  190. // feature
  191. setIntroduction(modelConfig.opening_statement!)
  192. setMoreLikeThisConfig(modelConfig.more_like_this || {
  193. enabled: false,
  194. })
  195. setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer || {
  196. enabled: false,
  197. })
  198. setSpeechToTextConfig(modelConfig.speech_to_text || {
  199. enabled: false,
  200. })
  201. setCitationConfig(modelConfig.retriever_resource || {
  202. enabled: false,
  203. })
  204. }
  205. const { hasSettedApiKey } = useProviderContext()
  206. const {
  207. currentModel: currModel,
  208. textGenerationModelList,
  209. } = useTextGenerationCurrentProviderAndModelAndModelList(
  210. {
  211. provider: modelConfig.provider,
  212. model: modelConfig.model_id,
  213. },
  214. )
  215. // Fill old app data missing model mode.
  216. useEffect(() => {
  217. if (hasFetchedDetail && !modelModeType) {
  218. const mode = currModel?.model_properties.mode as (ModelModeType | undefined)
  219. if (mode) {
  220. const newModelConfig = produce(modelConfig, (draft: ModelConfig) => {
  221. draft.mode = mode
  222. })
  223. setModelConfig(newModelConfig)
  224. }
  225. }
  226. }, [textGenerationModelList, hasFetchedDetail, modelModeType, currModel, modelConfig])
  227. const [promptMode, doSetPromptMode] = useState(PromptMode.simple)
  228. const isAdvancedMode = promptMode === PromptMode.advanced
  229. const [canReturnToSimpleMode, setCanReturnToSimpleMode] = useState(true)
  230. const setPromptMode = async (mode: PromptMode) => {
  231. if (mode === PromptMode.advanced) {
  232. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  233. await migrateToDefaultPrompt()
  234. setCanReturnToSimpleMode(true)
  235. }
  236. doSetPromptMode(mode)
  237. }
  238. const {
  239. chatPromptConfig,
  240. setChatPromptConfig,
  241. completionPromptConfig,
  242. setCompletionPromptConfig,
  243. currentAdvancedPrompt,
  244. setCurrentAdvancedPrompt,
  245. hasSetBlockStatus,
  246. setConversationHistoriesRole,
  247. migrateToDefaultPrompt,
  248. } = useAdvancedPromptConfig({
  249. appMode: mode,
  250. modelName: modelConfig.model_id,
  251. promptMode,
  252. modelModeType,
  253. prePrompt: modelConfig.configs.prompt_template,
  254. hasSetDataSet: dataSets.length > 0,
  255. onUserChangedPrompt: () => {
  256. setCanReturnToSimpleMode(false)
  257. },
  258. completionParams,
  259. setCompletionParams,
  260. setStop: setTempStop,
  261. })
  262. const setModel = async ({
  263. modelId,
  264. provider,
  265. mode: modeMode,
  266. features,
  267. }: { modelId: string; provider: string; mode: string; features: string[] }) => {
  268. if (isAdvancedMode) {
  269. const appMode = mode
  270. if (modeMode === ModelModeType.completion) {
  271. if (appMode === AppType.chat) {
  272. if (!completionPromptConfig.prompt.text || !completionPromptConfig.conversation_histories_role.assistant_prefix || !completionPromptConfig.conversation_histories_role.user_prefix)
  273. await migrateToDefaultPrompt(true, ModelModeType.completion)
  274. }
  275. else {
  276. if (!completionPromptConfig.prompt.text)
  277. await migrateToDefaultPrompt(true, ModelModeType.completion)
  278. }
  279. }
  280. if (modeMode === ModelModeType.chat) {
  281. if (chatPromptConfig.prompt.length === 0)
  282. await migrateToDefaultPrompt(true, ModelModeType.chat)
  283. }
  284. }
  285. const newModelConfig = produce(modelConfig, (draft: ModelConfig) => {
  286. draft.provider = provider
  287. draft.model_id = modelId
  288. draft.mode = modeMode as ModelModeType
  289. })
  290. setModelConfig(newModelConfig)
  291. const supportVision = features && features.includes(ModelFeatureEnum.vision)
  292. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  293. setVisionConfig({
  294. // eslint-disable-next-line @typescript-eslint/no-use-before-define
  295. ...visionConfig,
  296. enabled: supportVision,
  297. }, true)
  298. setCompletionParams({})
  299. }
  300. const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
  301. const [visionConfig, doSetVisionConfig] = useState({
  302. enabled: false,
  303. number_limits: 2,
  304. detail: Resolution.low,
  305. transfer_methods: [TransferMethod.local_file],
  306. })
  307. const setVisionConfig = (config: VisionSettings, notNoticeFormattingChanged?: boolean) => {
  308. doSetVisionConfig(config)
  309. if (!notNoticeFormattingChanged)
  310. setFormattingChanged(true)
  311. }
  312. useEffect(() => {
  313. fetchAppDetail({ url: '/apps', id: appId }).then(async (res: any) => {
  314. setMode(res.mode)
  315. const modelConfig = res.model_config
  316. const promptMode = modelConfig.prompt_type === PromptMode.advanced ? PromptMode.advanced : PromptMode.simple
  317. doSetPromptMode(promptMode)
  318. if (promptMode === PromptMode.advanced) {
  319. setChatPromptConfig(modelConfig.chat_prompt_config || clone(DEFAULT_CHAT_PROMPT_CONFIG) as any)
  320. setCompletionPromptConfig(modelConfig.completion_prompt_config || clone(DEFAULT_COMPLETION_PROMPT_CONFIG) as any)
  321. setCanReturnToSimpleMode(false)
  322. }
  323. const model = res.model_config.model
  324. let datasets: any = null
  325. if (modelConfig.agent_mode?.enabled)
  326. datasets = modelConfig.agent_mode?.tools.filter(({ dataset }: any) => dataset?.enabled)
  327. if (dataSets && datasets?.length && datasets?.length > 0) {
  328. const { data: dataSetsWithDetail } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasets.map(({ dataset }: any) => dataset.id) } })
  329. datasets = dataSetsWithDetail
  330. setDataSets(datasets)
  331. }
  332. setIntroduction(modelConfig.opening_statement)
  333. if (modelConfig.more_like_this)
  334. setMoreLikeThisConfig(modelConfig.more_like_this)
  335. if (modelConfig.suggested_questions_after_answer)
  336. setSuggestedQuestionsAfterAnswerConfig(modelConfig.suggested_questions_after_answer)
  337. if (modelConfig.speech_to_text)
  338. setSpeechToTextConfig(modelConfig.speech_to_text)
  339. if (modelConfig.retriever_resource)
  340. setCitationConfig(modelConfig.retriever_resource)
  341. if (modelConfig.annotation_reply)
  342. setAnnotationConfig(modelConfig.annotation_reply, true)
  343. if (modelConfig.sensitive_word_avoidance)
  344. setModerationConfig(modelConfig.sensitive_word_avoidance)
  345. if (modelConfig.external_data_tools)
  346. setExternalDataToolsConfig(modelConfig.external_data_tools)
  347. const config = {
  348. modelConfig: {
  349. provider: model.provider,
  350. model_id: model.name,
  351. mode: model.mode,
  352. configs: {
  353. prompt_template: modelConfig.pre_prompt,
  354. prompt_variables: userInputsFormToPromptVariables(modelConfig.user_input_form, modelConfig.dataset_query_variable),
  355. },
  356. opening_statement: modelConfig.opening_statement,
  357. more_like_this: modelConfig.more_like_this,
  358. suggested_questions_after_answer: modelConfig.suggested_questions_after_answer,
  359. speech_to_text: modelConfig.speech_to_text,
  360. retriever_resource: modelConfig.retriever_resource,
  361. sensitive_word_avoidance: modelConfig.sensitive_word_avoidance,
  362. external_data_tools: modelConfig.external_data_tools,
  363. dataSets: datasets || [],
  364. },
  365. completionParams: model.completion_params,
  366. }
  367. if (modelConfig.file_upload)
  368. setVisionConfig(modelConfig.file_upload.image, true)
  369. syncToPublishedConfig(config)
  370. setPublishedConfig(config)
  371. setDatasetConfigs({
  372. retrieval_model: RETRIEVE_TYPE.oneWay,
  373. ...modelConfig.dataset_configs,
  374. })
  375. setHasFetchedDetail(true)
  376. })
  377. }, [appId])
  378. const promptEmpty = (() => {
  379. if (mode === AppType.chat)
  380. return false
  381. if (isAdvancedMode) {
  382. if (modelModeType === ModelModeType.chat)
  383. return chatPromptConfig.prompt.every(({ text }) => !text)
  384. else
  385. return !completionPromptConfig.prompt.text
  386. }
  387. else { return !modelConfig.configs.prompt_template }
  388. })()
  389. const cannotPublish = (() => {
  390. if (mode === AppType.chat) {
  391. if (!isAdvancedMode)
  392. return false
  393. if (modelModeType === ModelModeType.completion) {
  394. if (!hasSetBlockStatus.history || !hasSetBlockStatus.query)
  395. return true
  396. return false
  397. }
  398. return false
  399. }
  400. else { return promptEmpty }
  401. })()
  402. const contextVarEmpty = mode === AppType.completion && dataSets.length > 0 && !hasSetContextVar
  403. const handlePublish = async (isSilence?: boolean) => {
  404. const modelId = modelConfig.model_id
  405. const promptTemplate = modelConfig.configs.prompt_template
  406. const promptVariables = modelConfig.configs.prompt_variables
  407. if (promptEmpty) {
  408. notify({ type: 'error', message: t('appDebug.otherError.promptNoBeEmpty'), duration: 3000 })
  409. return
  410. }
  411. if (isAdvancedMode && mode === AppType.chat) {
  412. if (modelModeType === ModelModeType.completion) {
  413. if (!hasSetBlockStatus.history) {
  414. notify({ type: 'error', message: t('appDebug.otherError.historyNoBeEmpty'), duration: 3000 })
  415. return
  416. }
  417. if (!hasSetBlockStatus.query) {
  418. notify({ type: 'error', message: t('appDebug.otherError.queryNoBeEmpty'), duration: 3000 })
  419. return
  420. }
  421. }
  422. }
  423. if (contextVarEmpty) {
  424. notify({ type: 'error', message: t('appDebug.feature.dataSet.queryVariable.contextVarNotEmpty'), duration: 3000 })
  425. return
  426. }
  427. const postDatasets = dataSets.map(({ id }) => ({
  428. dataset: {
  429. enabled: true,
  430. id,
  431. },
  432. }))
  433. // new model config data struct
  434. const data: BackendModelConfig = {
  435. // Simple Mode prompt
  436. pre_prompt: !isAdvancedMode ? promptTemplate : '',
  437. prompt_type: promptMode,
  438. chat_prompt_config: {},
  439. completion_prompt_config: {},
  440. user_input_form: promptVariablesToUserInputsForm(promptVariables),
  441. dataset_query_variable: contextVar || '',
  442. opening_statement: introduction || '',
  443. more_like_this: moreLikeThisConfig,
  444. suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig,
  445. speech_to_text: speechToTextConfig,
  446. retriever_resource: citationConfig,
  447. sensitive_word_avoidance: moderationConfig,
  448. external_data_tools: externalDataToolsConfig,
  449. agent_mode: {
  450. enabled: true,
  451. tools: [...postDatasets],
  452. },
  453. model: {
  454. provider: modelConfig.provider,
  455. name: modelId,
  456. mode: modelConfig.mode,
  457. completion_params: completionParams as any,
  458. },
  459. dataset_configs: datasetConfigs,
  460. file_upload: {
  461. image: visionConfig,
  462. },
  463. }
  464. if (isAdvancedMode) {
  465. data.chat_prompt_config = chatPromptConfig
  466. data.completion_prompt_config = completionPromptConfig
  467. }
  468. await updateAppModelConfig({ url: `/apps/${appId}/model-config`, body: data })
  469. const newModelConfig = produce(modelConfig, (draft: any) => {
  470. draft.opening_statement = introduction
  471. draft.more_like_this = moreLikeThisConfig
  472. draft.suggested_questions_after_answer = suggestedQuestionsAfterAnswerConfig
  473. draft.speech_to_text = speechToTextConfig
  474. draft.retriever_resource = citationConfig
  475. draft.dataSets = dataSets
  476. })
  477. setPublishedConfig({
  478. modelConfig: newModelConfig,
  479. completionParams,
  480. })
  481. if (!isSilence)
  482. notify({ type: 'success', message: t('common.api.success'), duration: 3000 })
  483. setCanReturnToSimpleMode(false)
  484. return true
  485. }
  486. const [showConfirm, setShowConfirm] = useState(false)
  487. const resetAppConfig = () => {
  488. syncToPublishedConfig(publishedConfig!)
  489. setShowConfirm(false)
  490. }
  491. const [showUseGPT4Confirm, setShowUseGPT4Confirm] = useState(false)
  492. const { locale } = useContext(I18n)
  493. if (isLoading) {
  494. return <div className='flex h-full items-center justify-center'>
  495. <Loading type='area' />
  496. </div>
  497. }
  498. return (
  499. <ConfigContext.Provider value={{
  500. appId,
  501. hasSetAPIKEY: hasSettedApiKey,
  502. isTrailFinished: false,
  503. mode,
  504. modelModeType,
  505. promptMode,
  506. isAdvancedMode,
  507. setPromptMode,
  508. canReturnToSimpleMode,
  509. setCanReturnToSimpleMode,
  510. chatPromptConfig,
  511. completionPromptConfig,
  512. currentAdvancedPrompt,
  513. setCurrentAdvancedPrompt,
  514. conversationHistoriesRole: completionPromptConfig.conversation_histories_role,
  515. showHistoryModal,
  516. setConversationHistoriesRole,
  517. hasSetBlockStatus,
  518. conversationId,
  519. introduction,
  520. setIntroduction,
  521. setConversationId,
  522. controlClearChatMessage,
  523. setControlClearChatMessage,
  524. prevPromptConfig,
  525. setPrevPromptConfig,
  526. moreLikeThisConfig,
  527. setMoreLikeThisConfig,
  528. suggestedQuestionsAfterAnswerConfig,
  529. setSuggestedQuestionsAfterAnswerConfig,
  530. speechToTextConfig,
  531. setSpeechToTextConfig,
  532. citationConfig,
  533. setCitationConfig,
  534. annotationConfig,
  535. setAnnotationConfig,
  536. moderationConfig,
  537. setModerationConfig,
  538. externalDataToolsConfig,
  539. setExternalDataToolsConfig,
  540. formattingChanged,
  541. setFormattingChanged,
  542. inputs,
  543. setInputs,
  544. query,
  545. setQuery,
  546. completionParams,
  547. setCompletionParams,
  548. modelConfig,
  549. setModelConfig,
  550. showSelectDataSet,
  551. dataSets,
  552. setDataSets,
  553. datasetConfigs,
  554. setDatasetConfigs,
  555. hasSetContextVar,
  556. isShowVisionConfig,
  557. visionConfig,
  558. setVisionConfig,
  559. }}
  560. >
  561. <>
  562. <div className="flex flex-col h-full">
  563. <div className='flex items-center justify-between px-6 shrink-0 py-3 flex-wrap gap-y-2'>
  564. <div className='flex items-end'>
  565. <div className={s.promptTitle}></div>
  566. <div className='flex items-center h-[14px] space-x-1 text-xs'>
  567. {/* modelModeType missing can not load template */}
  568. {(!isAdvancedMode && modelModeType) && (
  569. <div
  570. onClick={() => setPromptMode(PromptMode.advanced)}
  571. className={'cursor-pointer text-indigo-600'}
  572. >
  573. {t('appDebug.promptMode.simple')}
  574. </div>
  575. )}
  576. {isAdvancedMode && (
  577. <div className='flex items-center space-x-2'>
  578. <div className={cn(locale === 'en' && 'italic', `${s.advancedPromptMode} text-indigo-600`)}>{t('appDebug.promptMode.advanced')}</div>
  579. {canReturnToSimpleMode && (
  580. <div
  581. onClick={() => setPromptMode(PromptMode.simple)}
  582. className='flex items-center h-6 px-2 bg-indigo-600 shadow-xs border border-gray-200 rounded-lg text-white text-xs font-semibold cursor-pointer space-x-1'
  583. >
  584. <FlipBackward className='w-3 h-3 text-white' />
  585. <div className='text-xs font-semibold uppercase'>{t('appDebug.promptMode.switchBack')}</div>
  586. </div>
  587. )}
  588. </div>
  589. )}
  590. </div>
  591. </div>
  592. <div className='flex items-center flex-wrap gap-y-2 gap-x-2'>
  593. {/* Model and Parameters */}
  594. <ModelParameterModal
  595. isAdvancedMode={isAdvancedMode}
  596. mode={mode}
  597. provider={modelConfig.provider}
  598. completionParams={completionParams}
  599. modelId={modelConfig.model_id}
  600. setModel={setModel as any}
  601. onCompletionParamsChange={(newParams: FormValue) => {
  602. setCompletionParams(newParams)
  603. }}
  604. />
  605. <div className='w-[1px] h-[14px] bg-gray-200'></div>
  606. <Button onClick={() => setShowConfirm(true)} className='shrink-0 mr-2 w-[70px] !h-8 !text-[13px] font-medium'>{t('appDebug.operation.resetConfig')}</Button>
  607. {isMobile && (
  608. <Button className='!h-8 !text-[13px] font-medium' onClick={showDebugPanel}>
  609. <span className='mr-1'>{t('appDebug.operation.debugConfig')}</span>
  610. <CodeBracketIcon className="h-4 w-4 text-gray-500" />
  611. </Button>
  612. )}
  613. <Button type='primary' onClick={() => handlePublish(false)} className={cn(cannotPublish && '!bg-primary-200 !cursor-not-allowed', 'shrink-0 w-[70px] !h-8 !text-[13px] font-medium')}>{t('appDebug.operation.applyConfig')}</Button>
  614. </div>
  615. </div>
  616. <div className='flex grow h-[200px]'>
  617. <div className="w-full sm:w-1/2 shrink-0">
  618. <Config />
  619. </div>
  620. {!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)' }}>
  621. <Debug
  622. hasSetAPIKEY={hasSettedApiKey}
  623. onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
  624. inputs={inputs}
  625. />
  626. </div>}
  627. </div>
  628. </div>
  629. {showConfirm && (
  630. <Confirm
  631. title={t('appDebug.resetConfig.title')}
  632. content={t('appDebug.resetConfig.message')}
  633. isShow={showConfirm}
  634. onClose={() => setShowConfirm(false)}
  635. onConfirm={resetAppConfig}
  636. onCancel={() => setShowConfirm(false)}
  637. />
  638. )}
  639. {showUseGPT4Confirm && (
  640. <Confirm
  641. title={t('appDebug.trailUseGPT4Info.title')}
  642. content={t('appDebug.trailUseGPT4Info.description')}
  643. isShow={showUseGPT4Confirm}
  644. onClose={() => setShowUseGPT4Confirm(false)}
  645. onConfirm={() => {
  646. setShowAccountSettingModal({ payload: 'provider' })
  647. setShowUseGPT4Confirm(false)
  648. }}
  649. onCancel={() => setShowUseGPT4Confirm(false)}
  650. />
  651. )}
  652. {isShowSelectDataSet && (
  653. <SelectDataSet
  654. isShow={isShowSelectDataSet}
  655. onClose={hideSelectDataSet}
  656. selectedIds={selectedIds}
  657. onSelect={handleSelect}
  658. />
  659. )}
  660. {isShowHistoryModal && (
  661. <EditHistoryModal
  662. isShow={isShowHistoryModal}
  663. saveLoading={false}
  664. onClose={hideHistoryModal}
  665. data={completionPromptConfig.conversation_histories_role}
  666. onSave={(data) => {
  667. setConversationHistoriesRole(data)
  668. hideHistoryModal()
  669. }}
  670. />
  671. )}
  672. {isMobile && (
  673. <Drawer showClose isOpen={isShowDebugPanel} onClose={hideDebugPanel} mask footer={null} panelClassname='!bg-gray-50'>
  674. <Debug
  675. hasSetAPIKEY={hasSettedApiKey}
  676. onSetting={() => setShowAccountSettingModal({ payload: 'provider' })}
  677. inputs={inputs}
  678. />
  679. </Drawer>
  680. )}
  681. </>
  682. </ConfigContext.Provider>
  683. )
  684. }
  685. export default React.memo(Configuration)