Selaa lähdekoodia

chore: remove universal chat code (#2194)

Joel 1 vuosi sitten
vanhempi
commit
bec998ab94
26 muutettua tiedostoa jossa 10 lisäystä ja 1720 poistoa
  1. 0 13
      web/app/(commonLayout)/explore/chat/page.tsx
  2. 0 1
      web/app/components/app/configuration/dataset-config/card-item/index.tsx
  3. 1 1
      web/app/components/base/audio-btn/index.tsx
  4. 1 1
      web/app/components/base/voice-input/index.tsx
  5. 0 38
      web/app/components/explore/universal-chat/config-view/detail/index.tsx
  6. 0 9
      web/app/components/explore/universal-chat/config-view/detail/style.module.css
  7. 0 107
      web/app/components/explore/universal-chat/config-view/summary/index.tsx
  8. 0 21
      web/app/components/explore/universal-chat/config-view/summary/style.module.css
  9. 0 94
      web/app/components/explore/universal-chat/config/data-config/index.tsx
  10. 0 55
      web/app/components/explore/universal-chat/config/index.tsx
  11. 0 39
      web/app/components/explore/universal-chat/config/model-config/index.tsx
  12. 0 103
      web/app/components/explore/universal-chat/config/plugins-config/index.tsx
  13. 0 3
      web/app/components/explore/universal-chat/config/plugins-config/item.module.css
  14. 0 43
      web/app/components/explore/universal-chat/config/plugins-config/item.tsx
  15. 0 73
      web/app/components/explore/universal-chat/hooks/use-conversation.ts
  16. 0 831
      web/app/components/explore/universal-chat/index.tsx
  17. 0 43
      web/app/components/explore/universal-chat/init/index.tsx
  18. 0 9
      web/app/components/explore/universal-chat/init/style.module.css
  19. 0 1
      web/app/components/share/chat/index.tsx
  20. 6 19
      web/app/components/share/chat/sidebar/index.tsx
  21. 2 13
      web/app/components/share/chat/sidebar/list/index.tsx
  22. 0 2
      web/config/index.ts
  23. 0 39
      web/i18n/lang/explore.en.ts
  24. 0 39
      web/i18n/lang/explore.pt.ts
  25. 0 39
      web/i18n/lang/explore.zh.ts
  26. 0 84
      web/service/universal-chat.ts

+ 0 - 13
web/app/(commonLayout)/explore/chat/page.tsx

@@ -1,13 +0,0 @@
-import type { FC } from 'react'
-import React from 'react'
-import UniversalChat from '@/app/components/explore/universal-chat'
-
-const Chat: FC = () => {
-  return (
-    <div className='h-full p-2'>
-      <UniversalChat />
-    </div>
-  )
-}
-
-export default React.memo(Chat)

+ 0 - 1
web/app/components/app/configuration/dataset-config/card-item/index.tsx

@@ -16,7 +16,6 @@ export type ICardItemProps = {
   onRemove: (id: string) => void
   readonly?: boolean
 }
-// used in universal-chat
 const CardItem: FC<ICardItemProps> = ({
   className,
   config,

+ 1 - 1
web/app/components/base/audio-btn/index.tsx

@@ -33,7 +33,7 @@ const AudioBtn = ({
     if (value !== '') {
       formData.append('text', removeCodeBlocks(value))
 
-      let url = '/universal-chat/text-to-audio'
+      let url = ''
       let isPublic = false
 
       if (params.token) {

+ 1 - 1
web/app/components/base/voice-input/index.tsx

@@ -86,7 +86,7 @@ const VoiceInput = ({
     const formData = new FormData()
     formData.append('file', mp3File)
 
-    let url = '/universal-chat/audio-to-text'
+    let url = ''
     let isPublic = false
 
     if (params.token) {

+ 0 - 38
web/app/components/explore/universal-chat/config-view/detail/index.tsx

@@ -1,38 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import s from './style.module.css'
-import Config from '@/app/components/explore/universal-chat/config'
-import type { DataSet } from '@/models/datasets'
-
-type Props = {
-  modelId: string
-  providerName: string
-  plugins: Record<string, boolean>
-  dataSets: DataSet[]
-}
-const ConfigViewPanel: FC<Props> = ({
-  modelId,
-  providerName,
-  plugins,
-  dataSets,
-}) => {
-  const { t } = useTranslation()
-  return (
-    <div className={cn('absolute top-9 right-0 z-20 p-4 bg-white rounded-2xl shadow-md', s.panelBorder)}>
-      <div className='w-[368px]'>
-        <Config
-          readonly
-          modelId={modelId}
-          providerName={providerName}
-          plugins={plugins}
-          dataSets={dataSets}
-        />
-        <div className='mt-3 text-xs leading-[18px] text-500 font-normal'>{t('explore.universalChat.viewConfigDetailTip')}</div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(ConfigViewPanel)

+ 0 - 9
web/app/components/explore/universal-chat/config-view/detail/style.module.css

@@ -1,9 +0,0 @@
-.btn {
-  background: url(~@/assets/action.svg) center center no-repeat transparent;
-  background-size: 16px 16px;
-  /* mask-image: ; */
-}
-
-.panelBorder {
-  border: 0.5px solid rgba(0, 0, 0, .05);
-}

+ 0 - 107
web/app/components/explore/universal-chat/config-view/summary/index.tsx

@@ -1,107 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import { useBoolean, useClickAway } from 'ahooks'
-import s from './style.module.css'
-import ModelIcon from '@/app/components/header/account-setting/model-provider-page/model-icon'
-import ModelName from '@/app/components/header/account-setting/model-provider-page/model-name'
-import { Google, WebReader, Wikipedia } from '@/app/components/base/icons/src/public/plugins'
-import ConfigDetail from '@/app/components/explore/universal-chat/config-view/detail'
-import type { DataSet } from '@/models/datasets'
-import { useAgentThoughtCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
-
-export type ISummaryProps = {
-  modelId: string
-  providerName: string
-  plugins: Record<string, boolean>
-  dataSets: DataSet[]
-}
-
-const getColorInfo = (modelId: string) => {
-  if (modelId === 'gpt-4')
-    return s.gpt4
-
-  if (modelId === 'claude-2')
-    return s.claude
-
-  return s.gpt3
-}
-
-const getPlugIcon = (pluginId: string) => {
-  const className = 'w-4 h-4'
-  switch (pluginId) {
-    case 'google_search':
-      return <Google className={className} />
-    case 'web_reader':
-      return <WebReader className={className} />
-    case 'wikipedia':
-      return <Wikipedia className={className} />
-    default:
-      return null
-  }
-}
-
-const Summary: FC<ISummaryProps> = ({
-  modelId,
-  providerName,
-  plugins,
-  dataSets,
-}) => {
-  const {
-    currentModel: currModel,
-    currentProvider,
-  } = useAgentThoughtCurrentProviderAndModelAndModelList(
-    { provider: providerName, model: modelId },
-  )
-  // current_datetime is not configable and do not have icon
-  const pluginIds = Object.keys(plugins).filter(key => plugins[key] && key !== 'current_datetime')
-  const [isShowConfig, { setFalse: hideConfig, toggle: toggleShowConfig }] = useBoolean(false)
-  const configContentRef = React.useRef(null)
-
-  useClickAway(() => {
-    hideConfig()
-  }, configContentRef)
-  return (
-    <div ref={configContentRef} className='relative'>
-      <div onClick={toggleShowConfig} className={cn(getColorInfo(modelId), 'flex items-center px-1 h-8 rounded-lg border cursor-pointer')}>
-        <ModelIcon
-          provider={currentProvider}
-          modelName={currModel?.model}
-          className='!w-6 !h-6'
-        />
-        <div className='ml-2 text-[13px] font-medium text-gray-900'>
-          <ModelName
-            modelItem={currModel!}
-          />
-        </div>
-        {
-          pluginIds.length > 0 && (
-            <div className='ml-1.5 flex items-center'>
-              <div className='mr-1 h-3 w-[1px] bg-[#000] opacity-[0.05]'></div>
-              <div className='flex space-x-1'>
-                {pluginIds.map(pluginId => (
-                  <div
-                    key={pluginId}
-                    className={`flex items-center justify-center w-6 h-6 rounded-md ${s.border} bg-white`}
-                  >
-                    {getPlugIcon(pluginId)}</div>
-                ))}
-              </div>
-            </div>
-          )
-        }
-      </div>
-      {isShowConfig && (
-        <ConfigDetail
-          modelId={modelId}
-          providerName={providerName}
-          plugins={plugins}
-          dataSets={dataSets}
-        />
-      )}
-    </div>
-
-  )
-}
-export default React.memo(Summary)

+ 0 - 21
web/app/components/explore/universal-chat/config-view/summary/style.module.css

@@ -1,21 +0,0 @@
-.border {
-  border: 1px solid rgba(0, 0, 0, 0.05);
-}
-
-.gpt3 {
-  background: linear-gradient(0deg, #D3F8DF, #D3F8DF),
-linear-gradient(0deg, #EDFCF2, #EDFCF2);
-  border: 1px solid rgba(211, 248, 223, 1)
-}
-
-.gpt4 {
-  background: linear-gradient(0deg, #EBE9FE, #EBE9FE),
-  linear-gradient(0deg, #F4F3FF, #F4F3FF);
-  border: 1px solid rgba(235, 233, 254, 1)
-}
-
-.claude {
-  background: linear-gradient(0deg, #F9EBDF, #F9EBDF),
-linear-gradient(0deg, #FCF3EB, #FCF3EB);
-  border: 1px solid rgba(249, 235, 223, 1)
-}

+ 0 - 94
web/app/components/explore/universal-chat/config/data-config/index.tsx

@@ -1,94 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import { useBoolean } from 'ahooks'
-import { isEqual } from 'lodash-es'
-import produce from 'immer'
-import FeaturePanel from '@/app/components/app/configuration/base/feature-panel'
-import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
-import CardItem from '@/app/components/app/configuration/dataset-config/card-item'
-import SelectDataSet from '@/app/components/app/configuration/dataset-config/select-dataset'
-import type { DataSet } from '@/models/datasets'
-
-type Props = {
-  readonly?: boolean
-  dataSets: DataSet[]
-  onChange?: (data: DataSet[]) => void
-}
-
-const DatasetConfig: FC<Props> = ({
-  readonly,
-  dataSets,
-  onChange,
-}) => {
-  const { t } = useTranslation()
-
-  const selectedIds = dataSets.map(item => item.id)
-
-  const hasData = dataSets.length > 0
-  const [isShowSelectDataSet, { setTrue: showSelectDataSet, setFalse: hideSelectDataSet }] = useBoolean(false)
-  const handleSelect = (data: DataSet[]) => {
-    if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
-      hideSelectDataSet()
-      return
-    }
-
-    if (data.find(item => !item.name)) { // has not loaded selected dataset
-      const newSelected = produce(data, (draft) => {
-        data.forEach((item, index) => {
-          if (!item.name) { // not fetched database
-            const newItem = dataSets.find(i => i.id === item.id)
-            if (newItem)
-              draft[index] = newItem
-          }
-        })
-      })
-      onChange?.(newSelected)
-    }
-    else {
-      onChange?.(data)
-    }
-    hideSelectDataSet()
-  }
-  const onRemove = (id: string) => {
-    onChange?.(dataSets.filter(item => item.id !== id))
-  }
-
-  return (
-    <FeaturePanel
-      className='mt-3'
-      title={t('appDebug.feature.dataSet.title')}
-      headerRight={!readonly && <OperationBtn type="add" onClick={showSelectDataSet} />}
-      hasHeaderBottomBorder={!hasData}
-    >
-      {hasData
-        ? (
-          <div className='max-h-[220px] overflow-y-auto'>
-            {dataSets.map(item => (
-              <CardItem
-                className="mb-2 !w-full"
-                key={item.id}
-                config={item}
-                onRemove={onRemove}
-                readonly={readonly}
-              />
-            ))}
-          </div>
-        )
-        : (
-          <div className='pt-2 pb-1 text-xs text-gray-500'>{t('appDebug.feature.dataSet.noData')}</div>
-        )}
-
-      {isShowSelectDataSet && (
-        <SelectDataSet
-          isShow={isShowSelectDataSet}
-          onClose={hideSelectDataSet}
-          selectedIds={selectedIds}
-          onSelect={handleSelect}
-        />
-      )}
-    </FeaturePanel>
-  )
-}
-export default React.memo(DatasetConfig)

+ 0 - 55
web/app/components/explore/universal-chat/config/index.tsx

@@ -1,55 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import ModelConfig from './model-config'
-import DataConfig from './data-config'
-import PluginConfig from './plugins-config'
-import type { DataSet } from '@/models/datasets'
-
-export type IConfigProps = {
-  className?: string
-  readonly?: boolean
-  modelId: string
-  providerName: string
-  onModelChange?: (modelId: string, providerName: string) => void
-  plugins: Record<string, boolean>
-  onPluginChange?: (key: string, value: boolean) => void
-  dataSets: DataSet[]
-  onDataSetsChange?: (contexts: DataSet[]) => void
-}
-
-const Config: FC<IConfigProps> = ({
-  className,
-  readonly,
-  modelId,
-  providerName,
-  onModelChange,
-  plugins,
-  onPluginChange,
-  dataSets,
-  onDataSetsChange,
-}) => {
-  return (
-    <div className={className}>
-      <ModelConfig
-        readonly={readonly}
-        modelId={modelId}
-        providerName={providerName}
-        onChange={onModelChange}
-      />
-      <PluginConfig
-        readonly={readonly}
-        config={plugins}
-        onChange={onPluginChange}
-      />
-      {(!readonly || (readonly && dataSets.length > 0)) && (
-        <DataConfig
-          readonly={readonly}
-          dataSets={dataSets}
-          onChange={onDataSetsChange}
-        />
-      )}
-    </div>
-  )
-}
-export default React.memo(Config)

+ 0 - 39
web/app/components/explore/universal-chat/config/model-config/index.tsx

@@ -1,39 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
-import { useProviderContext } from '@/context/provider-context'
-
-export type IModelConfigProps = {
-  modelId: string
-  providerName: string
-  onChange?: (modelId: string, providerName: string) => void
-  readonly?: boolean
-}
-
-const ModelConfig: FC<IModelConfigProps> = ({
-  modelId,
-  providerName,
-  onChange,
-  readonly,
-}) => {
-  const { t } = useTranslation()
-  const { agentThoughtModelList } = useProviderContext()
-
-  return (
-    <div className='flex items-center justify-between h-[52px] px-3 rounded-xl bg-gray-50'>
-      <div className='text-sm font-semibold text-gray-800'>{t('explore.universalChat.model')}</div>
-      <ModelSelector
-        triggerClassName={`${readonly && '!cursor-not-allowed !opacity-60'}`}
-        defaultModel={{ provider: providerName, model: modelId }}
-        modelList={agentThoughtModelList}
-        onSelect={(model) => {
-          onChange?.(model.model, model.provider)
-        }}
-        readonly={readonly}
-      />
-    </div>
-  )
-}
-export default React.memo(ModelConfig)

+ 0 - 103
web/app/components/explore/universal-chat/config/plugins-config/index.tsx

@@ -1,103 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React, { useEffect } from 'react'
-import { useTranslation } from 'react-i18next'
-import Item from './item'
-import FeaturePanel from '@/app/components/app/configuration/base/feature-panel'
-import { Google, WebReader, Wikipedia } from '@/app/components/base/icons/src/public/plugins'
-import { getToolProviders } from '@/service/explore'
-import Loading from '@/app/components/base/loading'
-import { useModalContext } from '@/context/modal-context'
-
-export type IPluginsProps = {
-  readonly?: boolean
-  config: Record<string, boolean>
-  onChange?: (key: string, value: boolean) => void
-}
-
-const plugins = [
-  { key: 'google_search', icon: <Google /> },
-  { key: 'web_reader', icon: <WebReader /> },
-  { key: 'wikipedia', icon: <Wikipedia /> },
-] as const
-
-const Plugins: FC<IPluginsProps> = ({
-  readonly,
-  config,
-  onChange,
-}) => {
-  const { t } = useTranslation()
-  const { setShowAccountSettingModal } = useModalContext()
-  const [isLoading, setIsLoading] = React.useState(!readonly)
-  const [isSerpApiValid, setIsSerpApiValid] = React.useState(false)
-  const checkSerpApiKey = async () => {
-    if (readonly)
-      return
-
-    const provides: any = await getToolProviders()
-    const isSerpApiValid = !!provides.find((v: any) => v.tool_name === 'serpapi' && v.is_enabled)
-    setIsSerpApiValid(isSerpApiValid)
-    setIsLoading(false)
-  }
-  useEffect(() => {
-    checkSerpApiKey()
-  }, [])
-
-  const itemConfigs = plugins.map((plugin) => {
-    const res: Record<string, any> = { ...plugin }
-    const { key } = plugin
-    res.name = t(`explore.universalChat.plugins.${key}.name`)
-    if (key === 'web_reader')
-      res.description = t(`explore.universalChat.plugins.${key}.description`)
-
-    if (key === 'google_search' && !isSerpApiValid && !readonly) {
-      res.readonly = true
-      res.more = (
-        <div className='border-t border-[#FEF0C7] flex items-center h-[34px] pl-2 bg-[#FFFAEB] text-gray-700 text-xs '>
-          <span className='whitespace-pre'>{t('explore.universalChat.plugins.google_search.more.left')}</span>
-          <span className='cursor-pointer text-[#155EEF]' onClick={() => setShowAccountSettingModal({ payload: 'plugin', onCancelCallback: async () => await checkSerpApiKey() })}>{t('explore.universalChat.plugins.google_search.more.link')}</span>
-          <span className='whitespace-pre'>{t('explore.universalChat.plugins.google_search.more.right')}</span>
-        </div>
-      )
-    }
-    return res
-  })
-
-  const enabledPluginNum = Object.values(config).filter(v => v).length
-
-  return (
-    <>
-      <FeaturePanel
-        className='mt-3'
-        title={
-          <div className='flex space-x-1'>
-            <div>{t('explore.universalChat.plugins.name')}</div>
-            <div className='text-[13px] font-normal text-gray-500'>({enabledPluginNum}/{plugins.length})</div>
-          </div>}
-        hasHeaderBottomBorder={false}
-      >
-        {isLoading
-          ? (
-            <div className='flex items-center h-[166px]'>
-              <Loading type='area' />
-            </div>
-          )
-          : (<div className='space-y-2'>
-            {itemConfigs.map(item => (
-              <Item
-                key={item.key}
-                icon={item.icon}
-                name={item.name}
-                description={item.description}
-                more={item.more}
-                enabled={config[item.key]}
-                onChange={enabled => onChange?.(item.key, enabled)}
-                readonly={readonly || item.readonly}
-              />
-            ))}
-          </div>)}
-      </FeaturePanel>
-    </>
-  )
-}
-export default React.memo(Plugins)

+ 0 - 3
web/app/components/explore/universal-chat/config/plugins-config/item.module.css

@@ -1,3 +0,0 @@
-.shadow {
-  box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.05);
-}

+ 0 - 43
web/app/components/explore/universal-chat/config/plugins-config/item.tsx

@@ -1,43 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import cn from 'classnames'
-import s from './item.module.css'
-import Switch from '@/app/components/base/switch'
-
-export type IItemProps = {
-  icon: React.ReactNode
-  name: string
-  description?: string
-  more?: React.ReactNode
-  enabled: boolean
-  onChange: (enabled: boolean) => void
-  readonly?: boolean
-}
-
-const Item: FC<IItemProps> = ({
-  icon,
-  name,
-  description,
-  more,
-  enabled,
-  onChange,
-  readonly,
-}) => {
-  return (
-    <div className={cn('bg-white rounded-xl border border-gray-200 overflow-hidden', s.shadow)}>
-      <div className='flex justify-between items-center min-h-[48px] px-2'>
-        <div className='flex items-center space-x-2'>
-          {icon}
-          <div className='leading-[18px]'>
-            <div className='text-[13px] font-medium text-gray-800'>{name}</div>
-            {description && <div className='text-xs leading-[18px] text-gray-500'>{description}</div>}
-          </div>
-        </div>
-        <Switch size='md' defaultValue={enabled} onChange={onChange} disabled={readonly} />
-      </div>
-      {more}
-    </div>
-  )
-}
-export default React.memo(Item)

+ 0 - 73
web/app/components/explore/universal-chat/hooks/use-conversation.ts

@@ -1,73 +0,0 @@
-import { useState } from 'react'
-import produce from 'immer'
-import { useGetState } from 'ahooks'
-import type { ConversationItem } from '@/models/share'
-
-const storageConversationIdKey = 'conversationIdInfo'
-
-type ConversationInfoType = Omit<ConversationItem, 'inputs' | 'id'>
-function useConversation() {
-  const [conversationList, setConversationList] = useState<ConversationItem[]>([])
-  const [pinnedConversationList, setPinnedConversationList] = useState<ConversationItem[]>([])
-  const [currConversationId, doSetCurrConversationId, getCurrConversationId] = useGetState<string>('-1')
-  // when set conversation id, we do not have set appId
-  const setCurrConversationId = (id: string, appId: string, isSetToLocalStroge = true, newConversationName = '') => {
-    doSetCurrConversationId(id)
-    if (isSetToLocalStroge && id !== '-1') {
-      // conversationIdInfo: {[appId1]: conversationId1, [appId2]: conversationId2}
-      const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-      conversationIdInfo[appId] = id
-      globalThis.localStorage?.setItem(storageConversationIdKey, JSON.stringify(conversationIdInfo))
-    }
-  }
-
-  const getConversationIdFromStorage = (appId: string) => {
-    const conversationIdInfo = globalThis.localStorage?.getItem(storageConversationIdKey) ? JSON.parse(globalThis.localStorage?.getItem(storageConversationIdKey) || '') : {}
-    const id = conversationIdInfo[appId]
-    return id
-  }
-
-  const isNewConversation = currConversationId === '-1'
-  // input can be updated by user
-  const [newConversationInputs, setNewConversationInputs] = useState<Record<string, any> | null>(null)
-  const resetNewConversationInputs = () => {
-    if (!newConversationInputs)
-      return
-    setNewConversationInputs(produce(newConversationInputs, (draft) => {
-      Object.keys(draft).forEach((key) => {
-        draft[key] = ''
-      })
-    }))
-  }
-  const [existConversationInputs, setExistConversationInputs] = useState<Record<string, any> | null>(null)
-  const currInputs = isNewConversation ? newConversationInputs : existConversationInputs
-  const setCurrInputs = isNewConversation ? setNewConversationInputs : setExistConversationInputs
-
-  // info is muted
-  const [newConversationInfo, setNewConversationInfo] = useState<ConversationInfoType | null>(null)
-  const [existConversationInfo, setExistConversationInfo] = useState<ConversationInfoType | null>(null)
-  const currConversationInfo = isNewConversation ? newConversationInfo : existConversationInfo
-
-  return {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currInputs,
-    newConversationInputs,
-    existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    currConversationInfo,
-    setNewConversationInfo,
-    existConversationInfo,
-    setExistConversationInfo,
-  }
-}
-
-export default useConversation

+ 0 - 831
web/app/components/explore/universal-chat/index.tsx

@@ -1,831 +0,0 @@
-/* eslint-disable react-hooks/exhaustive-deps */
-/* eslint-disable @typescript-eslint/no-use-before-define */
-'use client'
-import type { FC } from 'react'
-import React, { useEffect, useRef, useState } from 'react'
-import cn from 'classnames'
-import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
-import produce from 'immer'
-import { useBoolean, useGetState } from 'ahooks'
-import AppUnavailable from '../../base/app-unavailable'
-import useConversation from './hooks/use-conversation'
-import Init from './init'
-import { ToastContext } from '@/app/components/base/toast'
-import Sidebar from '@/app/components/share/chat/sidebar'
-import {
-  delConversation,
-  fetchAppParams,
-  fetchChatList,
-  fetchConversations,
-  fetchSuggestedQuestions,
-  generationConversationName,
-  pinConversation,
-  sendChatMessage,
-  stopChatMessageResponding,
-  unpinConversation,
-  updateFeedback,
-} from '@/service/universal-chat'
-import type { ConversationItem, SiteInfo } from '@/models/share'
-import type { PromptConfig, SuggestedQuestionsAfterAnswerConfig } from '@/models/debug'
-import type { Feedbacktype, IChatItem } from '@/app/components/app/chat/type'
-import Chat from '@/app/components/app/chat'
-import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
-import Loading from '@/app/components/base/loading'
-import { replaceStringWithValues } from '@/app/components/app/configuration/prompt-value-panel'
-import { userInputsFormToPromptVariables } from '@/utils/model-config'
-import Confirm from '@/app/components/base/confirm'
-import type { DataSet } from '@/models/datasets'
-import ConfigSummary from '@/app/components/explore/universal-chat/config-view/summary'
-import { fetchDatasets } from '@/service/datasets'
-import ItemOperation from '@/app/components/explore/item-operation'
-import { useCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
-import { useProviderContext } from '@/context/provider-context'
-
-const APP_ID = 'universal-chat'
-const DEFAULT_PLUGIN = {
-  google_search: false,
-  web_reader: true,
-  wikipedia: true,
-}
-// Old configuration structure is not compatible with the current configuration
-localStorage.removeItem('universal-chat-config')
-const CONFIG_KEY = 'universal-chat-config-2'
-type CONFIG = {
-  providerName: string
-  modelId: string
-  plugin: {
-    google_search: boolean
-    web_reader: boolean
-    wikipedia: boolean
-  }
-}
-let prevConfig: null | CONFIG = localStorage.getItem(CONFIG_KEY) ? JSON.parse(localStorage.getItem(CONFIG_KEY) as string) as CONFIG : null
-const setPrevConfig = (config: CONFIG) => {
-  prevConfig = config
-  localStorage.setItem(CONFIG_KEY, JSON.stringify(prevConfig))
-}
-
-export type IMainProps = {}
-
-const Main: FC<IMainProps> = () => {
-  const { t } = useTranslation()
-  const media = useBreakpoints()
-  const isMobile = media === MediaType.mobile
-  const { agentThoughtModelList } = useProviderContext()
-  const getInitConfig = (type: 'model' | 'plugin') => {
-    if (type === 'model') {
-      return {
-        providerName: prevConfig?.providerName || agentThoughtModelList[0]?.provider,
-        modelId: prevConfig?.modelId || agentThoughtModelList[0]?.models[0]?.model,
-      }
-    }
-
-    if (type === 'plugin')
-      return prevConfig?.plugin || DEFAULT_PLUGIN
-  }
-
-  useEffect(() => {
-    document.title = `${t('explore.sidebar.chat')} -  Dify`
-  }, [])
-  /*
-  * app info
-  */
-  const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
-  const [isUnknwonReason, setIsUnknwonReason] = useState<boolean>(false)
-  const siteInfo: SiteInfo = (
-    {
-      title: 'universal Chatbot',
-      icon: '',
-      icon_background: '',
-      description: '',
-      default_language: 'en', // TODO
-      prompt_public: true,
-    }
-  )
-  const [promptConfig, setPromptConfig] = useState<PromptConfig | null>(null)
-  const [inited, setInited] = useState<boolean>(false)
-  // in mobile, show sidebar by click button
-  const [isShowSidebar, { setTrue: showSidebar, setFalse: hideSidebar }] = useBoolean(false)
-  /*
-  * conversation info
-  */
-  const [allConversationList, setAllConversationList] = useState<ConversationItem[]>([])
-  const [isClearConversationList, { setTrue: clearConversationListTrue, setFalse: clearConversationListFalse }] = useBoolean(false)
-  const [isClearPinnedConversationList, { setTrue: clearPinnedConversationListTrue, setFalse: clearPinnedConversationListFalse }] = useBoolean(false)
-  const {
-    conversationList,
-    setConversationList,
-    pinnedConversationList,
-    setPinnedConversationList,
-    currConversationId,
-    getCurrConversationId,
-    setCurrConversationId,
-    getConversationIdFromStorage,
-    isNewConversation,
-    currConversationInfo,
-    currInputs,
-    newConversationInputs,
-    // existConversationInputs,
-    resetNewConversationInputs,
-    setCurrInputs,
-    setNewConversationInfo,
-    existConversationInfo,
-    setExistConversationInfo,
-  } = useConversation()
-  const [hasMore, setHasMore] = useState<boolean>(true)
-  const [hasPinnedMore, setHasPinnedMore] = useState<boolean>(true)
-  const onMoreLoaded = ({ data: conversations, has_more }: any) => {
-    setHasMore(has_more)
-    if (isClearConversationList) {
-      setConversationList(conversations)
-      clearConversationListFalse()
-    }
-    else {
-      setConversationList([...conversationList, ...conversations])
-    }
-  }
-  const onPinnedMoreLoaded = ({ data: conversations, has_more }: any) => {
-    setHasPinnedMore(has_more)
-    if (isClearPinnedConversationList) {
-      setPinnedConversationList(conversations)
-      clearPinnedConversationListFalse()
-    }
-    else {
-      setPinnedConversationList([...pinnedConversationList, ...conversations])
-    }
-  }
-  const [controlUpdateConversationList, setControlUpdateConversationList] = useState(0)
-  const noticeUpdateList = () => {
-    setHasMore(true)
-    clearConversationListTrue()
-
-    setHasPinnedMore(true)
-    clearPinnedConversationListTrue()
-
-    setControlUpdateConversationList(Date.now())
-  }
-  const handlePin = async (id: string) => {
-    await pinConversation(id)
-    setControlItemOpHide(Date.now())
-    notify({ type: 'success', message: t('common.api.success') })
-    noticeUpdateList()
-  }
-
-  const handleUnpin = async (id: string) => {
-    await unpinConversation(id)
-    setControlItemOpHide(Date.now())
-    notify({ type: 'success', message: t('common.api.success') })
-    noticeUpdateList()
-  }
-  const [isShowConfirm, { setTrue: showConfirm, setFalse: hideConfirm }] = useBoolean(false)
-  const [toDeleteConversationId, setToDeleteConversationId] = useState('')
-  const handleDelete = (id: string) => {
-    setToDeleteConversationId(id)
-    hideSidebar() // mobile
-    showConfirm()
-  }
-
-  const didDelete = async () => {
-    await delConversation(toDeleteConversationId)
-    setControlItemOpHide(Date.now())
-    notify({ type: 'success', message: t('common.api.success') })
-    hideConfirm()
-    if (currConversationId === toDeleteConversationId)
-      handleConversationIdChange('-1')
-
-    noticeUpdateList()
-  }
-
-  const [suggestedQuestionsAfterAnswerConfig, setSuggestedQuestionsAfterAnswerConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [speechToTextConfig, setSpeechToTextConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-  const [citationConfig, setCitationConfig] = useState<SuggestedQuestionsAfterAnswerConfig | null>(null)
-
-  const [conversationIdChangeBecauseOfNew, setConversationIdChangeBecauseOfNew, getConversationIdChangeBecauseOfNew] = useGetState(false)
-
-  const conversationName = currConversationInfo?.name || t('share.chat.newChatDefaultName') as string
-  const conversationIntroduction = currConversationInfo?.introduction || ''
-
-  const handleConversationSwitch = async () => {
-    if (!inited)
-      return
-
-    // update inputs of current conversation
-    let notSyncToStateIntroduction = ''
-    let notSyncToStateInputs: Record<string, any> | undefined | null = {}
-    // debugger
-    if (!isNewConversation) {
-      const item = allConversationList.find(item => item.id === currConversationId) as any
-      notSyncToStateInputs = item?.inputs || {}
-      // setCurrInputs(notSyncToStateInputs)
-      notSyncToStateIntroduction = item?.introduction || ''
-      setExistConversationInfo({
-        name: item?.name || '',
-        introduction: notSyncToStateIntroduction,
-      })
-      const modelConfig = item?.model_config
-      if (modelConfig) {
-        setModeId(modelConfig.model_id)
-        const pluginConfig: Record<string, boolean> = {}
-        const datasetIds: string[] = []
-        modelConfig.agent_mode.tools.forEach((item: any) => {
-          const pluginName = Object.keys(item)[0]
-          if (pluginName === 'dataset')
-            datasetIds.push(item.dataset.id)
-          else
-            pluginConfig[pluginName] = item[pluginName].enabled
-        })
-        setPlugins(pluginConfig)
-        if (datasetIds.length > 0) {
-          const { data } = await fetchDatasets({ url: '/datasets', params: { page: 1, ids: datasetIds } })
-          setDateSets(data)
-        }
-        else {
-          setDateSets([])
-        }
-      }
-      else {
-        configSetDefaultValue()
-      }
-    }
-    else {
-      configSetDefaultValue()
-      notSyncToStateInputs = newConversationInputs
-      setCurrInputs(notSyncToStateInputs)
-    }
-
-    // update chat list of current conversation
-    if (!isNewConversation && !conversationIdChangeBecauseOfNew) {
-      fetchChatList(currConversationId).then((res: any) => {
-        const { data } = res
-        const newChatList: IChatItem[] = generateNewChatListWithOpenstatement(notSyncToStateIntroduction, notSyncToStateInputs)
-
-        data.forEach((item: any) => {
-          newChatList.push({
-            id: `question-${item.id}`,
-            content: item.query,
-            isAnswer: false,
-          })
-          newChatList.push({
-            ...item,
-            id: item.id,
-            content: item.answer,
-            feedback: item.feedback,
-            isAnswer: true,
-            citation: item.retriever_resources,
-          })
-        })
-        setChatList(newChatList)
-        setErrorHappened(false)
-      })
-    }
-
-    if (isNewConversation) {
-      setChatList(generateNewChatListWithOpenstatement())
-      setErrorHappened(false)
-    }
-
-    setControlFocus(Date.now())
-  }
-
-  useEffect(() => {
-    handleConversationSwitch()
-  }, [currConversationId, inited])
-
-  const handleConversationIdChange = (id: string) => {
-    if (id === '-1') {
-      createNewChat()
-      setConversationIdChangeBecauseOfNew(true)
-    }
-    else {
-      setConversationIdChangeBecauseOfNew(false)
-    }
-    // trigger handleConversationSwitch
-    setCurrConversationId(id, APP_ID)
-    setIsShowSuggestion(false)
-    hideSidebar()
-  }
-
-  /*
-  * chat info. chat is under conversation.
-  */
-  const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
-  const chatListDomRef = useRef<HTMLDivElement>(null)
-  useEffect(() => {
-    // scroll to bottom
-    if (chatListDomRef.current)
-      chatListDomRef.current.scrollTop = chatListDomRef.current.scrollHeight
-  }, [chatList, currConversationId])
-
-  // user can not edit inputs if user had send message
-  const createNewChat = async () => {
-    // if new chat is already exist, do not create new chat
-    abortController?.abort()
-    setResponsingFalse()
-    if (conversationList.some(item => item.id === '-1'))
-      return
-
-    setConversationList(produce(conversationList, (draft) => {
-      draft.unshift({
-        id: '-1',
-        name: t('share.chat.newChatDefaultName'),
-        inputs: newConversationInputs,
-        introduction: conversationIntroduction,
-      })
-    }))
-    configSetDefaultValue()
-  }
-
-  // sometime introduction is not applied to state
-  const generateNewChatListWithOpenstatement = (introduction?: string, inputs?: Record<string, any> | null) => {
-    let caculatedIntroduction = introduction || conversationIntroduction || ''
-    const caculatedPromptVariables = inputs || currInputs || null
-    if (caculatedIntroduction && caculatedPromptVariables)
-      caculatedIntroduction = replaceStringWithValues(caculatedIntroduction, promptConfig?.prompt_variables || [], caculatedPromptVariables)
-
-    const openstatement = {
-      id: `${Date.now()}`,
-      content: caculatedIntroduction,
-      isAnswer: true,
-      feedbackDisabled: true,
-      isOpeningStatement: true,
-    }
-    if (caculatedIntroduction)
-      return [openstatement]
-
-    return []
-  }
-
-  const fetchAllConversations = () => {
-    return fetchConversations(undefined, undefined, 100)
-  }
-
-  const fetchInitData = async () => {
-    return Promise.all([fetchAllConversations(), fetchAppParams()])
-  }
-
-  // init
-  useEffect(() => {
-    (async () => {
-      try {
-        const [conversationData, appParams]: any = await fetchInitData()
-        const prompt_template = ''
-        // handle current conversation id
-        const { data: allConversations } = conversationData as { data: ConversationItem[]; has_more: boolean }
-        const _conversationId = getConversationIdFromStorage(APP_ID)
-        const isNotNewConversation = allConversations.some(item => item.id === _conversationId)
-        setAllConversationList(allConversations)
-        // fetch new conversation info
-        const { user_input_form, opening_statement: introduction, suggested_questions_after_answer, speech_to_text, retriever_resource }: any = appParams
-        const prompt_variables = userInputsFormToPromptVariables(user_input_form)
-
-        setNewConversationInfo({
-          name: t('share.chat.newChatDefaultName'),
-          introduction,
-        })
-        setPromptConfig({
-          prompt_template,
-          prompt_variables,
-        } as PromptConfig)
-        setSuggestedQuestionsAfterAnswerConfig(suggested_questions_after_answer)
-        setSpeechToTextConfig(speech_to_text)
-        setCitationConfig(retriever_resource)
-
-        if (isNotNewConversation)
-          setCurrConversationId(_conversationId, APP_ID, false)
-
-        setInited(true)
-      }
-      catch (e: any) {
-        if (e.status === 404) {
-          setAppUnavailable(true)
-        }
-        else {
-          setIsUnknwonReason(true)
-          setAppUnavailable(true)
-        }
-      }
-    })()
-  }, [])
-
-  const [isResponsing, { setTrue: setResponsingTrue, setFalse: setResponsingFalse }] = useBoolean(false)
-  const [abortController, setAbortController] = useState<AbortController | null>(null)
-  const { notify } = useContext(ToastContext)
-  const logError = (message: string) => {
-    notify({ type: 'error', message })
-  }
-
-  const checkCanSend = () => {
-    if (currConversationId !== '-1')
-      return true
-
-    const prompt_variables = promptConfig?.prompt_variables
-    const inputs = currInputs
-    if (!inputs || !prompt_variables || prompt_variables?.length === 0)
-      return true
-
-    let hasEmptyInput = false
-    const requiredVars = prompt_variables?.filter(({ key, name, required }) => {
-      const res = (!key || !key.trim()) || (!name || !name.trim()) || (required || required === undefined || required === null)
-      return res
-    }) || [] // compatible with old version
-    requiredVars.forEach(({ key }) => {
-      if (hasEmptyInput)
-        return
-
-      if (!inputs?.[key])
-        hasEmptyInput = true
-    })
-
-    if (hasEmptyInput) {
-      logError(t('appDebug.errorMessage.valueOfVarRequired'))
-      return false
-    }
-    return !hasEmptyInput
-  }
-
-  const [controlFocus, setControlFocus] = useState(0)
-  const [isShowSuggestion, setIsShowSuggestion] = useState(false)
-  const doShowSuggestion = isShowSuggestion && !isResponsing
-  const [suggestQuestions, setSuggestQuestions] = useState<string[]>([])
-  const [messageTaskId, setMessageTaskId] = useState('')
-  const [hasStopResponded, setHasStopResponded, getHasStopResponded] = useGetState(false)
-  const [errorHappened, setErrorHappened] = useState(false)
-  const [isResponsingConIsCurrCon, setIsResponsingConCurrCon, getIsResponsingConIsCurrCon] = useGetState(true)
-  const initConfig = getInitConfig('model')
-  const [modelId, setModeId] = useState<string>((initConfig as any)?.modelId as string)
-  const [providerName, setProviderName] = useState<string>((initConfig as any)?.providerName)
-  const { currentModel } = useCurrentProviderAndModel(
-    agentThoughtModelList,
-    { provider: providerName, model: modelId },
-  )
-  const handleSend = async (message: string) => {
-    if (isNewConversation) {
-      const isModelSelected = modelId && !!currentModel
-      if (!isModelSelected) {
-        notify({ type: 'error', message: t('appDebug.errorMessage.notSelectModel') })
-        return
-      }
-      setPrevConfig({
-        modelId,
-        providerName,
-        plugin: plugins as any,
-      })
-    }
-
-    if (isResponsing) {
-      notify({ type: 'info', message: t('appDebug.errorMessage.waitForResponse') })
-      return
-    }
-    const formattedPlugins = Object.keys(plugins).map(key => ({
-      [key]: {
-        enabled: plugins[key],
-      },
-    }))
-    const formattedDataSets = dataSets.map(({ id }) => {
-      return {
-        dataset: {
-          enabled: true,
-          id,
-        },
-      }
-    })
-    const data = {
-      query: message,
-      conversation_id: isNewConversation ? null : currConversationId,
-      model: modelId,
-      provider: providerName,
-      tools: [...formattedPlugins, ...formattedDataSets],
-    }
-
-    // qustion
-    const questionId = `question-${Date.now()}`
-    const questionItem = {
-      id: questionId,
-      content: message,
-      agent_thoughts: [],
-      isAnswer: false,
-    }
-
-    const placeholderAnswerId = `answer-placeholder-${Date.now()}`
-    const placeholderAnswerItem = {
-      id: placeholderAnswerId,
-      content: '',
-      isAnswer: true,
-    }
-
-    const newList = [...getChatList(), questionItem, placeholderAnswerItem]
-    setChatList(newList)
-
-    // answer
-    const responseItem: IChatItem = {
-      id: `${Date.now()}`,
-      content: '',
-      agent_thoughts: [],
-      isAnswer: true,
-    }
-
-    const prevTempNewConversationId = getCurrConversationId() || '-1'
-    let tempNewConversationId = prevTempNewConversationId
-
-    setHasStopResponded(false)
-    setResponsingTrue()
-    setErrorHappened(false)
-    setIsShowSuggestion(false)
-    setIsResponsingConCurrCon(true)
-
-    sendChatMessage(data, {
-      getAbortController: (abortController) => {
-        setAbortController(abortController)
-      },
-      onData: (message: string, isFirstMessage: boolean, { conversationId: newConversationId, messageId, taskId }: any) => {
-        responseItem.content = responseItem.content + message
-        responseItem.id = messageId
-        if (isFirstMessage && newConversationId)
-          tempNewConversationId = newConversationId
-
-        setMessageTaskId(taskId)
-        // has switched to other conversation
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsResponsingConCurrCon(false)
-          return
-        }
-
-        // closesure new list is outdated.
-        const newListWithAnswer = produce(
-          getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
-          (draft) => {
-            if (!draft.find(item => item.id === questionId))
-              draft.push({ ...questionItem } as any)
-
-            draft.push({ ...responseItem })
-          })
-
-        setChatList(newListWithAnswer)
-      },
-      async onCompleted(hasError?: boolean) {
-        if (hasError) {
-          setResponsingFalse()
-          return
-        }
-
-        if (getConversationIdChangeBecauseOfNew()) {
-          const { data: allConversations }: any = await fetchAllConversations()
-          const newItem: any = await generationConversationName(allConversations[0].id)
-          const newAllConversations = produce(allConversations, (draft: any) => {
-            draft[0].name = newItem.name
-          })
-          setAllConversationList(newAllConversations as any)
-          noticeUpdateList()
-        }
-        setConversationIdChangeBecauseOfNew(false)
-        resetNewConversationInputs()
-        setCurrConversationId(tempNewConversationId, APP_ID, true)
-        if (getIsResponsingConIsCurrCon() && suggestedQuestionsAfterAnswerConfig?.enabled && !getHasStopResponded()) {
-          const { data }: any = await fetchSuggestedQuestions(responseItem.id)
-          setSuggestQuestions(data)
-          setIsShowSuggestion(true)
-        }
-        setResponsingFalse()
-      },
-      onThought(thought) {
-        // thought finished then start to return message. Warning: use push agent_thoughts.push would caused problem when the thought is more then 2
-        responseItem.id = thought.message_id;
-        (responseItem as any).agent_thoughts = [...(responseItem as any).agent_thoughts, thought] // .push(thought)
-        // has switched to other conversation
-
-        if (prevTempNewConversationId !== getCurrConversationId()) {
-          setIsResponsingConCurrCon(false)
-          return
-        }
-        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)
-      },
-      onMessageEnd: (messageEnd) => {
-        responseItem.citation = messageEnd.metadata?.retriever_resources
-
-        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)
-      },
-      onError() {
-        setErrorHappened(true)
-        // role back placeholder answer
-        setChatList(produce(getChatList(), (draft) => {
-          draft.splice(draft.findIndex(item => item.id === placeholderAnswerId), 1)
-        }))
-        setResponsingFalse()
-      },
-    })
-  }
-
-  const handleFeedback = async (messageId: string, feedback: Feedbacktype) => {
-    await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating } })
-    const newChatList = chatList.map((item) => {
-      if (item.id === messageId) {
-        return {
-          ...item,
-          feedback,
-        }
-      }
-      return item
-    })
-    setChatList(newChatList)
-    notify({ type: 'success', message: t('common.api.success') })
-  }
-
-  const [controlChatUpdateAllConversation, setControlChatUpdateAllConversation] = useState(0)
-  useEffect(() => {
-    (async () => {
-      if (controlChatUpdateAllConversation && !isNewConversation) {
-        const { data: allConversations } = await fetchAllConversations() as { data: ConversationItem[]; has_more: boolean }
-        const item = allConversations.find(item => item.id === currConversationId)
-        setAllConversationList(allConversations)
-        if (item) {
-          setExistConversationInfo({
-            ...existConversationInfo,
-            name: item?.name || '',
-          } as any)
-        }
-      }
-    })()
-  }, [controlChatUpdateAllConversation])
-  const renderSidebar = () => {
-    if (!APP_ID || !promptConfig)
-      return null
-    return (
-      <Sidebar
-        list={conversationList}
-        onListChanged={(list) => {
-          setConversationList(list)
-          setControlChatUpdateAllConversation(Date.now())
-        }}
-        isClearConversationList={isClearConversationList}
-        pinnedList={pinnedConversationList}
-        onPinnedListChanged={(list) => {
-          setPinnedConversationList(list)
-          setControlChatUpdateAllConversation(Date.now())
-        }}
-        isClearPinnedConversationList={isClearPinnedConversationList}
-        onMoreLoaded={onMoreLoaded}
-        onPinnedMoreLoaded={onPinnedMoreLoaded}
-        isNoMore={!hasMore}
-        isPinnedNoMore={!hasPinnedMore}
-        onCurrentIdChange={handleConversationIdChange}
-        currentId={currConversationId}
-        copyRight={''}
-        isInstalledApp={false}
-        isUniversalChat
-        installedAppId={''}
-        siteInfo={siteInfo}
-        onPin={handlePin}
-        onUnpin={handleUnpin}
-        controlUpdateList={controlUpdateConversationList}
-        onDelete={handleDelete}
-        onStartChat={() => handleConversationIdChange('-1')}
-      />
-    )
-  }
-  // const currModel = MODEL_LIST.find(item => item.id === modelId)
-
-  const [plugins, setPlugins] = useState<Record<string, boolean>>(getInitConfig('plugin') as Record<string, boolean>)
-  const handlePluginsChange = (key: string, value: boolean) => {
-    setPlugins({
-      ...plugins,
-      [key]: value,
-    })
-  }
-  const [dataSets, setDateSets] = useState<DataSet[]>([])
-  const configSetDefaultValue = () => {
-    const initConfig = getInitConfig('model')
-    setModeId((initConfig as any)?.modelId as string)
-    setProviderName((initConfig as any)?.providerName)
-    setPlugins(getInitConfig('plugin') as any)
-    setDateSets([])
-  }
-  const isCurrConversationPinned = !!pinnedConversationList.find(item => item.id === currConversationId)
-  const [controlItemOpHide, setControlItemOpHide] = useState(0)
-  if (appUnavailable)
-    return <AppUnavailable isUnknwonReason={isUnknwonReason} />
-
-  if (!promptConfig)
-    return <Loading type='app' />
-
-  return (
-    <div className='bg-gray-100 h-full'>
-      <div
-        className={cn(
-          'flex rounded-t-2xl bg-white overflow-hidden rounded-b-2xl h-full',
-        )}
-        style={{
-          boxShadow: '0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03)',
-        }}
-      >
-        {/* sidebar */}
-        {!isMobile && renderSidebar()}
-        {isMobile && isShowSidebar && (
-          <div className='fixed inset-0 z-50'
-            style={{ backgroundColor: 'rgba(35, 56, 118, 0.2)' }}
-            onClick={hideSidebar}
-          >
-            <div className='inline-block' onClick={e => e.stopPropagation()}>
-              {renderSidebar()}
-            </div>
-          </div>
-        )}
-        {/* main */}
-        <div className={cn(
-          'h-full flex-grow flex flex-col overflow-y-auto',
-        )
-        }>
-          {(!isNewConversation || isResponsing || errorHappened) && (
-            <div className='mb-5 antialiased font-sans shrink-0 relative mobile:min-h-[48px] tablet:min-h-[64px]'>
-              <div className='absolute z-10 top-0 left-0 right-0 flex items-center justify-between border-b border-gray-100 mobile:h-12 tablet:h-16 px-8 bg-white'>
-                <div className='text-gray-900'>{conversationName}</div>
-                <div className='flex items-center shrink-0 ml-2 space-x-2'>
-                  <ConfigSummary
-                    modelId={modelId}
-                    providerName={providerName}
-                    plugins={plugins}
-                    dataSets={dataSets}
-                  />
-                  <div className={cn('flex w-8 h-8 justify-center items-center shrink-0 rounded-lg border border-gray-200')} onClick={e => e.stopPropagation()}>
-                    <ItemOperation
-                      key={controlItemOpHide}
-                      className='!w-8 !h-8'
-                      isPinned={isCurrConversationPinned}
-                      togglePin={() => isCurrConversationPinned ? handleUnpin(currConversationId) : handlePin(currConversationId)}
-                      isShowDelete
-                      onDelete={() => handleDelete(currConversationId)}
-                    />
-                  </div>
-                </div>
-              </div>
-            </div>
-          )}
-          <div className={cn(doShowSuggestion ? 'pb-[140px]' : (isResponsing ? 'pb-[113px]' : 'pb-[76px]'), 'relative grow h-[200px] pc:w-[794px] max-w-full mobile:w-full mx-auto  mb-3.5 overflow-hidden')}>
-            <div className={cn('pc:w-[794px] max-w-full mobile:w-full mx-auto h-full overflow-y-auto')} ref={chatListDomRef}>
-              <Chat
-                isShowConfigElem={isNewConversation && chatList.length === 0}
-                configElem={<Init
-                  modelId={modelId}
-                  providerName={providerName}
-                  onModelChange={(modelId, providerName) => {
-                    setModeId(modelId)
-                    setProviderName(providerName)
-                  }}
-                  plugins={plugins}
-                  onPluginChange={handlePluginsChange}
-                  dataSets={dataSets}
-                  onDataSetsChange={setDateSets}
-                />}
-                chatList={chatList}
-                onSend={handleSend}
-                isHideFeedbackEdit
-                onFeedback={handleFeedback}
-                isResponsing={isResponsing}
-                canStopResponsing={!!messageTaskId && isResponsingConIsCurrCon}
-                abortResponsing={async () => {
-                  await stopChatMessageResponding(messageTaskId)
-                  setHasStopResponded(true)
-                  setResponsingFalse()
-                }}
-                checkCanSend={checkCanSend}
-                controlFocus={controlFocus}
-                isShowSuggestion={doShowSuggestion}
-                suggestionList={suggestQuestions}
-                isShowSpeechToText={speechToTextConfig?.enabled}
-                isShowCitation={citationConfig?.enabled}
-                dataSets={dataSets}
-              />
-            </div>
-          </div>
-
-          {isShowConfirm && (
-            <Confirm
-              title={t('share.chat.deleteConversation.title')}
-              content={t('share.chat.deleteConversation.content')}
-              isShow={isShowConfirm}
-              onClose={hideConfirm}
-              onConfirm={didDelete}
-              onCancel={hideConfirm}
-            />
-          )}
-        </div>
-      </div>
-    </div>
-  )
-}
-export default React.memo(Main)

+ 0 - 43
web/app/components/explore/universal-chat/init/index.tsx

@@ -1,43 +0,0 @@
-'use client'
-import type { FC } from 'react'
-import React from 'react'
-import { useTranslation } from 'react-i18next'
-import cn from 'classnames'
-import type { IConfigProps } from '../config'
-import Config from '../config'
-import s from './style.module.css'
-
-const Line = (
-  <svg width="100%" height="1" viewBox="0 0 720 1" fill="none" xmlns="http://www.w3.org/2000/svg">
-    <line y1="0.5" x2="720" y2="0.5" stroke="url(#paint0_linear_6845_53470)"/>
-    <defs>
-      <linearGradient id="paint0_linear_6845_53470" x1="0" y1="1" x2="720" y2="1" gradientUnits="userSpaceOnUse">
-        <stop stopColor="#F2F4F7" stopOpacity="0"/>
-        <stop offset="0.491667" stopColor="#F2F4F7"/>
-        <stop offset="1" stopColor="#F2F4F7" stopOpacity="0"/>
-      </linearGradient>
-    </defs>
-  </svg>
-)
-
-const Init: FC<IConfigProps> = ({
-  ...configProps
-}) => {
-  const { t } = useTranslation()
-
-  return (
-    <div className='h-full flex items-center justify-center'>
-      <div>
-        <div className='text-center'>
-          <div className={cn(s.textGradient, 'mb-2 leading-[32px] font-semibold text-[24px]')}>{t('explore.universalChat.welcome')}</div>
-          <div className='mb-2 font-normal text-sm text-gray-500'>{t('explore.universalChat.welcomeDescribe')}</div>
-        </div>
-        <div className='flex mb-2 h-8 items-center'>
-          {Line}
-        </div>
-        <Config {...configProps} />
-      </div>
-    </div>
-  )
-}
-export default React.memo(Init)

+ 0 - 9
web/app/components/explore/universal-chat/init/style.module.css

@@ -1,9 +0,0 @@
-.textGradient {
-  background: linear-gradient(to right, rgba(16, 74, 225, 1) 0, rgba(0, 152, 238, 1) 100%);
-  -webkit-background-clip: text;
-  -webkit-text-fill-color: transparent;
-  background-clip: text;
-  text-fill-color: transparent;
-}
-
-

+ 0 - 1
web/app/components/share/chat/index.tsx

@@ -58,7 +58,6 @@ export type IMainProps = {
   isInstalledApp?: boolean
   installedAppInfo?: InstalledApp
   isSupportPlugin?: boolean
-  isUniversalChat?: boolean
 }
 
 const Main: FC<IMainProps> = ({

+ 6 - 19
web/app/components/share/chat/sidebar/index.tsx

@@ -11,7 +11,6 @@ import AppInfo from '@/app/components/share/chat/sidebar/app-info'
 // import Card from './card'
 import type { ConversationItem, SiteInfo } from '@/models/share'
 import { fetchConversations } from '@/service/share'
-import { fetchConversations as fetchUniversalConversations } from '@/service/universal-chat'
 
 export type ISidebarProps = {
   copyRight: string
@@ -25,7 +24,6 @@ export type ISidebarProps = {
   isClearPinnedConversationList: boolean
   isInstalledApp: boolean
   installedAppId?: string
-  isUniversalChat?: boolean
   siteInfo: SiteInfo
   onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
   onPinnedMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
@@ -50,7 +48,6 @@ const Sidebar: FC<ISidebarProps> = ({
   isClearPinnedConversationList,
   isInstalledApp,
   installedAppId,
-  isUniversalChat,
   siteInfo,
   onMoreLoaded,
   onPinnedMoreLoaded,
@@ -66,13 +63,7 @@ const Sidebar: FC<ISidebarProps> = ({
   const [hasPinned, setHasPinned] = useState(false)
 
   const checkHasPinned = async () => {
-    let res: any
-    if (isUniversalChat)
-      res = await fetchUniversalConversations(undefined, true)
-
-    else
-      res = await fetchConversations(isInstalledApp, installedAppId, undefined, true)
-
+    const res = await fetchConversations(isInstalledApp, installedAppId, undefined, true) as any
     setHasPinned(res.data.length > 0)
   }
 
@@ -85,13 +76,13 @@ const Sidebar: FC<ISidebarProps> = ({
       checkHasPinned()
   }, [controlUpdateList])
 
-  const maxListHeight = (isInstalledApp || isUniversalChat) ? 'max-h-[30vh]' : 'max-h-[40vh]'
+  const maxListHeight = (isInstalledApp) ? 'max-h-[30vh]' : 'max-h-[40vh]'
 
   return (
     <div
       className={
         cn(
-          (isInstalledApp || isUniversalChat) ? 'tablet:h-[calc(100vh_-_74px)]' : '',
+          (isInstalledApp) ? 'tablet:h-[calc(100vh_-_74px)]' : '',
           'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px]  border-r border-gray-200 mobile:h-screen',
         )
       }
@@ -125,7 +116,6 @@ const Sidebar: FC<ISidebarProps> = ({
               isClearConversationList={isClearPinnedConversationList}
               isInstalledApp={isInstalledApp}
               installedAppId={installedAppId}
-              isUniversalChat={isUniversalChat}
               onMoreLoaded={onPinnedMoreLoaded}
               isNoMore={isPinnedNoMore}
               isPinned={true}
@@ -149,7 +139,6 @@ const Sidebar: FC<ISidebarProps> = ({
             isClearConversationList={isClearConversationList}
             isInstalledApp={isInstalledApp}
             installedAppId={installedAppId}
-            isUniversalChat={isUniversalChat}
             onMoreLoaded={onMoreLoaded}
             isNoMore={isNoMore}
             isPinned={false}
@@ -160,11 +149,9 @@ const Sidebar: FC<ISidebarProps> = ({
         </div>
 
       </div>
-      {!isUniversalChat && (
-        <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
-          <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
-        </div>
-      )}
+      <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
+        <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
+      </div>
     </div>
   )
 }

+ 2 - 13
web/app/components/share/chat/sidebar/list/index.tsx

@@ -9,7 +9,6 @@ import RenameModal from '../rename-modal'
 import Item from './item'
 import type { ConversationItem } from '@/models/share'
 import { fetchConversations, renameConversation } from '@/service/share'
-import { fetchConversations as fetchUniversalConversations, renameConversation as renameUniversalConversation } from '@/service/universal-chat'
 import Toast from '@/app/components/base/toast'
 
 export type IListProps = {
@@ -20,7 +19,6 @@ export type IListProps = {
   onListChanged?: (newList: ConversationItem[]) => void
   isClearConversationList: boolean
   isInstalledApp: boolean
-  isUniversalChat?: boolean
   installedAppId?: string
   onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
   isNoMore: boolean
@@ -38,7 +36,6 @@ const List: FC<IListProps> = ({
   onListChanged,
   isClearConversationList,
   isInstalledApp,
-  isUniversalChat,
   installedAppId,
   onMoreLoaded,
   isNoMore,
@@ -56,11 +53,7 @@ const List: FC<IListProps> = ({
         let lastId = !isClearConversationList ? list[list.length - 1]?.id : undefined
         if (lastId === '-1')
           lastId = undefined
-        let res: any
-        if (isUniversalChat)
-          res = await fetchUniversalConversations(lastId, isPinned)
-        else
-          res = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned)
+        const res = await fetchConversations(isInstalledApp, installedAppId, lastId, isPinned) as any
         const { data: conversations, has_more }: any = res
         onMoreLoaded({ data: conversations, has_more })
       }
@@ -93,11 +86,7 @@ const List: FC<IListProps> = ({
     setIsSaving()
     const currId = currentConversation.id
     try {
-      if (isUniversalChat)
-        await renameUniversalConversation(currId, newName)
-
-      else
-        await renameConversation(isInstalledApp, installedAppId, currId, newName)
+      await renameConversation(isInstalledApp, installedAppId, currId, newName)
 
       Toast.notify({
         type: 'success',

+ 0 - 2
web/config/index.ts

@@ -52,8 +52,6 @@ export const MODEL_LIST = [
   { id: 'claude-instant-1', name: 'claude-instant-1', type: AppType.completion, provider: ProviderType.anthropic }, // set 30k
   { id: 'claude-2', name: 'claude-2', type: AppType.completion, provider: ProviderType.anthropic }, // set 30k
 ]
-const UNIVERSAL_CHAT_MODEL_ID_LIST = ['gpt-3.5-turbo', 'gpt-3.5-turbo-16k', 'gpt-4', 'claude-2']
-export const UNIVERSAL_CHAT_MODEL_LIST = MODEL_LIST.filter(({ id, type }) => UNIVERSAL_CHAT_MODEL_ID_LIST.includes(id) && (type === AppType.chat))
 export const TONE_LIST = [
   {
     id: 1,

+ 0 - 39
web/i18n/lang/explore.en.ts

@@ -36,45 +36,6 @@ const translation = {
     Programming: 'Programming',
     HR: 'HR',
   },
-  universalChat: {
-    welcome: 'Start chat with Dify',
-    welcomeDescribe: 'Your AI conversation companion for personalized assistance',
-    model: 'Model',
-    plugins: {
-      name: 'Plugins',
-      google_search: {
-        name: 'Google Search',
-        more: {
-          left: 'Enable the plugin, ',
-          link: 'set up your SerpAPI key',
-          right: ' first.',
-        },
-      },
-      web_reader: {
-        name: 'Web Reader',
-        description: 'Get needed information from any web link',
-      },
-      wikipedia: {
-        name: 'Wikipedia',
-      },
-    },
-    thought: {
-      show: 'Show',
-      hide: 'Hide',
-      processOfThought: ' the process of thinking',
-      res: {
-        webReader: {
-          normal: 'Reading {url}',
-          hasPageInfo: 'Reading next page of {url}',
-        },
-        google: 'Searching Google {{query}}',
-        wikipedia: 'Searching Wikipedia {{query}}',
-        dataset: 'Retrieving Knowledge {datasetName}',
-        date: 'Searching date',
-      },
-    },
-    viewConfigDetailTip: 'In conversation, cannot change above settings',
-  },
 }
 
 export default translation

+ 0 - 39
web/i18n/lang/explore.pt.ts

@@ -36,45 +36,6 @@ const translation = {
     Programming: 'Programação',
     HR: 'RH',
   },
-  universalChat: {
-    welcome: 'Iniciar chat com Dify',
-    welcomeDescribe: 'Seu companheiro de conversa de IA para assistência personalizada',
-    model: 'Modelo',
-    plugins: {
-      name: 'Plugins',
-      google_search: {
-        name: 'Pesquisa do Google',
-        more: {
-          left: 'Ative o plugin, ',
-          link: 'configure sua chave SerpAPI',
-          right: ' primeiro.',
-        },
-      },
-      web_reader: {
-        name: 'Leitor da Web',
-        description: 'Obtenha informações necessárias de qualquer link da web',
-      },
-      wikipedia: {
-        name: 'Wikipedia',
-      },
-    },
-    thought: {
-      show: 'Mostrar',
-      hide: 'Ocultar',
-      processOfThought: ' o processo de pensamento',
-      res: {
-        webReader: {
-          normal: 'Lendo {url}',
-          hasPageInfo: 'Lendo próxima página de {url}',
-        },
-        google: 'Pesquisando no Google {{query}}',
-        wikipedia: 'Pesquisando na Wikipedia {{query}}',
-        dataset: 'Recuperando Conhecimento {datasetName}',
-        date: 'Pesquisando data',
-      },
-    },
-    viewConfigDetailTip: 'Na conversa, não é possível alterar as configurações acima',
-  },
 }
 
 export default translation

+ 0 - 39
web/i18n/lang/explore.zh.ts

@@ -36,45 +36,6 @@ const translation = {
     Programming: '编程',
     HR: '人力资源',
   },
-  universalChat: {
-    welcome: '开始和 Dify 聊天吧',
-    welcomeDescribe: '您的 AI 对话伴侣,为您提供个性化的帮助',
-    model: '模型',
-    plugins: {
-      name: '插件',
-      google_search: {
-        name: '谷歌搜索',
-        more: {
-          left: '启用插件,首先',
-          link: '设置您的 SerpAPI 密钥',
-          right: '',
-        },
-      },
-      web_reader: {
-        name: '解析链接',
-        description: '从任何网页链接获取所需信息',
-      },
-      wikipedia: {
-        name: '维基百科',
-      },
-    },
-    thought: {
-      show: '显示',
-      hide: '隐藏',
-      processOfThought: '思考过程',
-      res: {
-        webReader: {
-          normal: '解析链接 {url}',
-          hasPageInfo: '解析链接 {url} 的下一页',
-        },
-        google: '搜索谷歌 {{query}}',
-        wikipedia: '搜索维基百科 {{query}}',
-        dataset: '检索知识库 {datasetName}',
-        date: '查询日期',
-      },
-    },
-    viewConfigDetailTip: '在对话中,无法更改上述设置',
-  },
 }
 
 export default translation

+ 0 - 84
web/service/universal-chat.ts

@@ -1,84 +0,0 @@
-import type { IOnCompleted, IOnData, IOnError, IOnMessageEnd, IOnThought } from './base'
-import {
-  del, get, patch, post, ssePost,
-} from './base'
-import type { Feedbacktype } from '@/app/components/app/chat/type'
-
-const baseUrl = 'universal-chat'
-
-function getUrl(url: string) {
-  return `${baseUrl}/${url.startsWith('/') ? url.slice(1) : url}`
-}
-
-export const sendChatMessage = async (body: Record<string, any>, { onData, onCompleted, onError, onThought, onMessageEnd, getAbortController }: {
-  onData: IOnData
-  onCompleted: IOnCompleted
-  onError: IOnError
-  onThought: IOnThought
-  onMessageEnd: IOnMessageEnd
-  getAbortController?: (abortController: AbortController) => void
-}) => {
-  return ssePost(getUrl('messages'), {
-    body: {
-      ...body,
-      response_mode: 'streaming',
-    },
-  }, { onData, onCompleted, onThought, onError, getAbortController, onMessageEnd })
-}
-
-export const stopChatMessageResponding = async (taskId: string) => {
-  return post(getUrl(`messages/${taskId}/stop`))
-}
-
-export const fetchConversations = async (last_id?: string, pinned?: boolean, limit?: number) => {
-  return get(getUrl('conversations'), { params: { ...{ limit: limit || 20 }, ...(last_id ? { last_id } : {}), ...(pinned !== undefined ? { pinned } : {}) } })
-}
-
-export const pinConversation = async (id: string) => {
-  return patch(getUrl(`conversations/${id}/pin`))
-}
-
-export const unpinConversation = async (id: string) => {
-  return patch(getUrl(`conversations/${id}/unpin`))
-}
-
-export const delConversation = async (id: string) => {
-  return del(getUrl(`conversations/${id}`))
-}
-
-export const renameConversation = async (id: string, name: string) => {
-  return post(getUrl(`conversations/${id}/name`), { body: { name } })
-}
-
-export const generationConversationName = async (id: string) => {
-  return post(getUrl(`conversations/${id}/name`), { body: { auto_generate: true } })
-}
-
-export const fetchChatList = async (conversationId: string) => {
-  return get(getUrl('messages'), { params: { conversation_id: conversationId, limit: 20, last_id: '' } })
-}
-
-// init value. wait for server update
-export const fetchAppParams = async () => {
-  return get(getUrl('parameters'))
-}
-
-export const updateFeedback = async ({ url, body }: { url: string; body: Feedbacktype }) => {
-  return post(getUrl(url), { body })
-}
-
-export const fetchMoreLikeThis = async (messageId: string) => {
-  return get(getUrl(`/messages/${messageId}/more-like-this`), {
-    params: {
-      response_mode: 'blocking',
-    },
-  })
-}
-
-export const fetchSuggestedQuestions = (messageId: string) => {
-  return get(getUrl(`/messages/${messageId}/suggested-questions`))
-}
-
-export const audioToText = (url: string, body: FormData) => {
-  return post(url, { body }, { bodyStringify: false, deleteContentType: true }) as Promise<{ text: string }>
-}