Commit b5695337 authored by StyleZhang's avatar StyleZhang

Merge branch 'feat/universal-chat-fe' into deploy/dev

parents 10376aba e828217c
...@@ -6,6 +6,7 @@ import GA, { GaType } from '@/app/components/base/ga' ...@@ -6,6 +6,7 @@ import GA, { GaType } from '@/app/components/base/ga'
import HeaderWrapper from '@/app/components/header/HeaderWrapper' import HeaderWrapper from '@/app/components/header/HeaderWrapper'
import Header from '@/app/components/header' import Header from '@/app/components/header'
import { EventEmitterContextProvider } from '@/context/event-emitter' import { EventEmitterContextProvider } from '@/context/event-emitter'
import { ProviderContextProvider } from '@/context/provider-context'
const Layout = ({ children }: { children: ReactNode }) => { const Layout = ({ children }: { children: ReactNode }) => {
return ( return (
...@@ -14,10 +15,12 @@ const Layout = ({ children }: { children: ReactNode }) => { ...@@ -14,10 +15,12 @@ const Layout = ({ children }: { children: ReactNode }) => {
<SwrInitor> <SwrInitor>
<AppContextProvider> <AppContextProvider>
<EventEmitterContextProvider> <EventEmitterContextProvider>
<ProviderContextProvider>
<HeaderWrapper> <HeaderWrapper>
<Header /> <Header />
</HeaderWrapper> </HeaderWrapper>
{children} {children}
</ProviderContextProvider>
</EventEmitterContextProvider> </EventEmitterContextProvider>
</AppContextProvider> </AppContextProvider>
</SwrInitor> </SwrInitor>
......
...@@ -44,9 +44,10 @@ export type IAnswerProps = { ...@@ -44,9 +44,10 @@ export type IAnswerProps = {
isResponsing?: boolean isResponsing?: boolean
answerIconClassName?: string answerIconClassName?: string
thoughts?: ThoughtItem[] thoughts?: ThoughtItem[]
isThinking?: boolean
} }
// The component needs to maintain its own state to control whether to display input component // The component needs to maintain its own state to control whether to display input component
const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedbackEdit = false, onFeedback, onSubmitAnnotation, displayScene = 'web', isResponsing, answerIconClassName, thoughts }) => { const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedbackEdit = false, onFeedback, onSubmitAnnotation, displayScene = 'web', isResponsing, answerIconClassName, thoughts, isThinking }) => {
const { id, content, more, feedback, adminFeedback, annotation: initAnnotation } = item const { id, content, more, feedback, adminFeedback, annotation: initAnnotation } = item
const [showEdit, setShowEdit] = useState(false) const [showEdit, setShowEdit] = useState(false)
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
...@@ -204,7 +205,7 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba ...@@ -204,7 +205,7 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
: ( : (
<div> <div>
{(thoughts && thoughts.length > 0) && ( {(thoughts && thoughts.length > 0) && (
<Thought list={thoughts || []}/> <Thought list={thoughts || []} isThinking={isThinking} />
)} )}
<Markdown content={content} /> <Markdown content={content} />
</div> </div>
......
...@@ -47,6 +47,7 @@ export type IChatProps = { ...@@ -47,6 +47,7 @@ export type IChatProps = {
isShowSpeechToText?: boolean isShowSpeechToText?: boolean
answerIconClassName?: string answerIconClassName?: string
isShowConfigElem?: boolean isShowConfigElem?: boolean
isThoughting?: boolean
} }
const Chat: FC<IChatProps> = ({ const Chat: FC<IChatProps> = ({
...@@ -156,6 +157,7 @@ const Chat: FC<IChatProps> = ({ ...@@ -156,6 +157,7 @@ const Chat: FC<IChatProps> = ({
if (item.isAnswer) { if (item.isAnswer) {
const isLast = item.id === chatList[chatList.length - 1].id const isLast = item.id === chatList[chatList.length - 1].id
const thoughts = item.agent_thoughts?.filter(item => item.thought !== '[DONE]') const thoughts = item.agent_thoughts?.filter(item => item.thought !== '[DONE]')
const isThinking = item.agent_thoughts && item.agent_thoughts?.length > 0 && !item.agent_thoughts.find(item => item.thought !== '[DONE]')
return <Answer return <Answer
key={item.id} key={item.id}
item={item} item={item}
...@@ -167,6 +169,7 @@ const Chat: FC<IChatProps> = ({ ...@@ -167,6 +169,7 @@ const Chat: FC<IChatProps> = ({
isResponsing={isResponsing && isLast} isResponsing={isResponsing && isLast}
answerIconClassName={answerIconClassName} answerIconClassName={answerIconClassName}
thoughts={thoughts} thoughts={thoughts}
isThinking={isThinking}
/> />
} }
return <Question key={item.id} id={item.id} content={item.content} more={item.more} useCurrentUserAvatar={useCurrentUserAvatar} /> return <Question key={item.id} id={item.id} content={item.content} more={item.more} useCurrentUserAvatar={useCurrentUserAvatar} />
......
...@@ -5,13 +5,14 @@ import cn from 'classnames' ...@@ -5,13 +5,14 @@ import cn from 'classnames'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import type { ThoughtItem } from '../type' import type { ThoughtItem } from '../type'
import s from './style.module.css' import s from './style.module.css'
import { DataSet, Search, ThoughtList, WebReader } from '@/app/components/base/icons/src/public/thought' import { DataSet, Loading as LodingIcon, Search, ThoughtList, WebReader } from '@/app/components/base/icons/src/public/thought'
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows' import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
// https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/ // https://www.freecodecamp.org/news/how-to-write-a-regular-expression-for-a-url/
const urlRegex = /(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?\/[a-zA-Z0-9]{2,}|((https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?)|(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?/gi const urlRegex = /(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?\/[a-zA-Z0-9]{2,}|((https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z]{2,}(\.[a-zA-Z]{2,})(\.[a-zA-Z]{2,})?)|(https:\/\/www\.|http:\/\/www\.|https:\/\/|http:\/\/)?[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}\.[a-zA-Z0-9]{2,}(\.[a-zA-Z0-9]{2,})?/gi
export type IThoughtProps = { export type IThoughtProps = {
list: ThoughtItem[] list: ThoughtItem[]
isThinking?: boolean
} }
const getIcon = (toolId: string) => { const getIcon = (toolId: string) => {
...@@ -27,6 +28,7 @@ const getIcon = (toolId: string) => { ...@@ -27,6 +28,7 @@ const getIcon = (toolId: string) => {
const Thought: FC<IThoughtProps> = ({ const Thought: FC<IThoughtProps> = ({
list, list,
isThinking,
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [isShowDetail, setIsShowDetail] = React.useState(false) const [isShowDetail, setIsShowDetail] = React.useState(false)
...@@ -62,8 +64,11 @@ const Thought: FC<IThoughtProps> = ({ ...@@ -62,8 +64,11 @@ const Thought: FC<IThoughtProps> = ({
return ( return (
<div className={cn(s.wrap, !isShowDetail && s.wrapHoverEffect, 'inline-block mb-2 px-2 py-0.5 rounded-md text-xs text-gray-500 font-medium')} > <div className={cn(s.wrap, !isShowDetail && s.wrapHoverEffect, 'inline-block mb-2 px-2 py-0.5 rounded-md text-xs text-gray-500 font-medium')} >
<div className='flex items-center h-6 space-x-1 cursor-pointer' onClick={() => setIsShowDetail(!isShowDetail)} > <div className='flex items-center h-6 space-x-1 cursor-pointer' onClick={() => setIsShowDetail(!isShowDetail)} >
<ThoughtList /> {!isThinking ? <ThoughtList /> : <div className='animate-spin'><LodingIcon /></div>}
<div>{t(`explore.universalChat.thought.${isShowDetail ? 'hide' : 'show'}`)}{t('explore.universalChat.thought.processOfThought')}</div> <div dangerouslySetInnerHTML= {{
__html: isThinking ? getThoughtText(list[0]) : (t(`explore.universalChat.thought.${isShowDetail ? 'hide' : 'show'}`) + t('explore.universalChat.thought.processOfThought')),
}}
></div>
<ChevronDown className={isShowDetail ? 'rotate-180' : '' } /> <ChevronDown className={isShowDetail ? 'rotate-180' : '' } />
</div> </div>
{isShowDetail && ( {isShowDetail && (
......
...@@ -21,6 +21,7 @@ export type ThoughtItem = { ...@@ -21,6 +21,7 @@ export type ThoughtItem = {
tool: string // plugin or dataset tool: string // plugin or dataset
thought: string thought: string
tool_input: string tool_input: string
message_id: string
} }
export type IChatItem = { export type IChatItem = {
id: string id: string
......
...@@ -4,7 +4,6 @@ import React from 'react' ...@@ -4,7 +4,6 @@ import React from 'react'
import { useContext } from 'use-context-selector' import { useContext } from 'use-context-selector'
import produce from 'immer' import produce from 'immer'
import { useBoolean } from 'ahooks' import { useBoolean } from 'ahooks'
import useSWR from 'swr'
import DatasetConfig from '../dataset-config' import DatasetConfig from '../dataset-config'
import ChatGroup from '../features/chat-group' import ChatGroup from '../features/chat-group'
import ExperienceEnchanceGroup from '../features/experience-enchance-group' import ExperienceEnchanceGroup from '../features/experience-enchance-group'
...@@ -20,7 +19,7 @@ import ConfigPrompt from '@/app/components/app/configuration/config-prompt' ...@@ -20,7 +19,7 @@ import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
import ConfigVar from '@/app/components/app/configuration/config-var' import ConfigVar from '@/app/components/app/configuration/config-var'
import type { PromptVariable } from '@/models/debug' import type { PromptVariable } from '@/models/debug'
import { AppType } from '@/types/app' import { AppType } from '@/types/app'
import { fetchTenantInfo } from '@/service/common' import { useProviderContext } from '@/context/provider-context'
const Config: FC = () => { const Config: FC = () => {
const { const {
...@@ -39,8 +38,7 @@ const Config: FC = () => { ...@@ -39,8 +38,7 @@ const Config: FC = () => {
setSpeechToTextConfig, setSpeechToTextConfig,
} = useContext(ConfigContext) } = useContext(ConfigContext)
const isChatApp = mode === AppType.chat const isChatApp = mode === AppType.chat
const { data: userInfo } = useSWR({ url: '/info' }, fetchTenantInfo) const { currentProvider } = useProviderContext()
const openaiProvider = userInfo?.providers?.find(({ token_is_set, is_valid, provider_name }) => token_is_set && is_valid && provider_name === 'openai')
const promptTemplate = modelConfig.configs.prompt_template const promptTemplate = modelConfig.configs.prompt_template
const promptVariables = modelConfig.configs.prompt_variables const promptVariables = modelConfig.configs.prompt_variables
...@@ -92,7 +90,7 @@ const Config: FC = () => { ...@@ -92,7 +90,7 @@ const Config: FC = () => {
}, },
}) })
const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && openaiProvider)) const hasChatConfig = isChatApp && (featureConfig.openingStatement || featureConfig.suggestedQuestionsAfterAnswer || (featureConfig.speechToText && currentProvider?.provider_name === 'openai'))
const hasToolbox = false const hasToolbox = false
const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false) const [showAutomatic, { setTrue: showAutomaticTrue, setFalse: showAutomaticFalse }] = useBoolean(false)
...@@ -122,7 +120,7 @@ const Config: FC = () => { ...@@ -122,7 +120,7 @@ const Config: FC = () => {
isChatApp={isChatApp} isChatApp={isChatApp}
config={featureConfig} config={featureConfig}
onChange={handleFeatureChange} onChange={handleFeatureChange}
showSpeechToTextItem={!!openaiProvider} showSpeechToTextItem={currentProvider?.provider_name === 'openai'}
/> />
)} )}
{showAutomatic && ( {showAutomatic && (
...@@ -162,7 +160,7 @@ const Config: FC = () => { ...@@ -162,7 +160,7 @@ const Config: FC = () => {
} }
} }
isShowSuggestedQuestionsAfterAnswer={featureConfig.suggestedQuestionsAfterAnswer} isShowSuggestedQuestionsAfterAnswer={featureConfig.suggestedQuestionsAfterAnswer}
isShowSpeechText={featureConfig.speechToText} isShowSpeechText={featureConfig.speechToText && currentProvider?.provider_name === 'openai'}
/> />
) )
} }
......
...@@ -22,6 +22,7 @@ import type { ModelConfig as BackendModelConfig } from '@/types/app' ...@@ -22,6 +22,7 @@ import type { ModelConfig as BackendModelConfig } from '@/types/app'
import { promptVariablesToUserInputsForm } from '@/utils/model-config' import { promptVariablesToUserInputsForm } from '@/utils/model-config'
import TextGeneration from '@/app/components/app/text-generate/item' import TextGeneration from '@/app/components/app/text-generate/item'
import { IS_CE_EDITION } from '@/config' import { IS_CE_EDITION } from '@/config'
import { useProviderContext } from '@/context/provider-context'
type IDebug = { type IDebug = {
hasSetAPIKEY: boolean hasSetAPIKEY: boolean
...@@ -51,7 +52,7 @@ const Debug: FC<IDebug> = ({ ...@@ -51,7 +52,7 @@ const Debug: FC<IDebug> = ({
modelConfig, modelConfig,
completionParams, completionParams,
} = useContext(ConfigContext) } = useContext(ConfigContext)
const { currentProvider } = useProviderContext()
const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([]) const [chatList, setChatList, getChatList] = useGetState<IChatItem[]>([])
const chatListDomRef = useRef<HTMLDivElement>(null) const chatListDomRef = useRef<HTMLDivElement>(null)
useEffect(() => { useEffect(() => {
...@@ -389,7 +390,7 @@ const Debug: FC<IDebug> = ({ ...@@ -389,7 +390,7 @@ const Debug: FC<IDebug> = ({
}} }}
isShowSuggestion={doShowSuggestion} isShowSuggestion={doShowSuggestion}
suggestionList={suggestQuestions} suggestionList={suggestQuestions}
isShowSpeechToText={speechToTextConfig.enabled} isShowSpeechToText={speechToTextConfig.enabled && currentProvider?.provider_name === 'openai'}
/> />
</div> </div>
</div> </div>
......
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_7998_4025)">
<path d="M6 1.125V2.375M6 9V11M2.875 6H1.125M10.625 6H9.875M9.22855 9.22855L8.875 8.875M9.33211 2.70789L8.625 3.415M2.46079 9.53921L3.875 8.125M2.56434 2.60434L3.625 3.665" stroke="#667085" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"/>
</g>
<defs>
<clipPath id="clip0_7998_4025">
<rect width="12" height="12" fill="white"/>
</clipPath>
</defs>
</svg>
{
"icon": {
"type": "element",
"isRootNode": true,
"name": "svg",
"attributes": {
"width": "12",
"height": "12",
"viewBox": "0 0 12 12",
"fill": "none",
"xmlns": "http://www.w3.org/2000/svg"
},
"children": [
{
"type": "element",
"name": "g",
"attributes": {
"clip-path": "url(#clip0_7998_4025)"
},
"children": [
{
"type": "element",
"name": "path",
"attributes": {
"d": "M6 1.125V2.375M6 9V11M2.875 6H1.125M10.625 6H9.875M9.22855 9.22855L8.875 8.875M9.33211 2.70789L8.625 3.415M2.46079 9.53921L3.875 8.125M2.56434 2.60434L3.625 3.665",
"stroke": "#667085",
"stroke-width": "1.25",
"stroke-linecap": "round",
"stroke-linejoin": "round"
},
"children": []
}
]
},
{
"type": "element",
"name": "defs",
"attributes": {},
"children": [
{
"type": "element",
"name": "clipPath",
"attributes": {
"id": "clip0_7998_4025"
},
"children": [
{
"type": "element",
"name": "rect",
"attributes": {
"width": "12",
"height": "12",
"fill": "white"
},
"children": []
}
]
}
]
}
]
},
"name": "Loading"
}
\ No newline at end of file
// GENERATE BY script
// DON NOT EDIT IT MANUALLY
import * as React from 'react'
import data from './Loading.json'
import IconBase from '@/app/components/base/icons/IconBase'
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
props,
ref,
) => <IconBase {...props} ref={ref} data={data as IconData} />)
export default Icon
export { default as DataSet } from './DataSet' export { default as DataSet } from './DataSet'
export { default as Loading } from './Loading'
export { default as Search } from './Search' export { default as Search } from './Search'
export { default as ThoughtList } from './ThoughtList' export { default as ThoughtList } from './ThoughtList'
export { default as WebReader } from './WebReader' export { default as WebReader } from './WebReader'
...@@ -4,6 +4,7 @@ import React from 'react' ...@@ -4,6 +4,7 @@ import React from 'react'
import cn from 'classnames' import cn from 'classnames'
import { useBoolean, useClickAway } from 'ahooks' import { useBoolean, useClickAway } from 'ahooks'
import { ChevronDownIcon } from '@heroicons/react/24/outline' import { ChevronDownIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import ModelIcon from '@/app/components/app/configuration/config-model/model-icon' import ModelIcon from '@/app/components/app/configuration/config-model/model-icon'
import { UNIVERSAL_CHAT_MODEL_LIST as MODEL_LIST } from '@/config' import { UNIVERSAL_CHAT_MODEL_LIST as MODEL_LIST } from '@/config'
...@@ -18,6 +19,8 @@ const ModelConfig: FC<IModelConfigProps> = ({ ...@@ -18,6 +19,8 @@ const ModelConfig: FC<IModelConfigProps> = ({
onChange, onChange,
readonly, readonly,
}) => { }) => {
const { t } = useTranslation()
const currModel = MODEL_LIST.find(item => item.id === modelId) const currModel = MODEL_LIST.find(item => item.id === modelId)
const [isShowOption, { setFalse: hideOption, toggle: toogleOption }] = useBoolean(false) const [isShowOption, { setFalse: hideOption, toggle: toogleOption }] = useBoolean(false)
const triggerRef = React.useRef(null) const triggerRef = React.useRef(null)
...@@ -27,7 +30,7 @@ const ModelConfig: FC<IModelConfigProps> = ({ ...@@ -27,7 +30,7 @@ const ModelConfig: FC<IModelConfigProps> = ({
return ( return (
<div className='flex items-center justify-between h-[52px] px-3 rounded-xl bg-gray-50'> <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'>Model</div> <div className='text-sm font-semibold text-gray-800'>{t('explore.universalChat.model')}</div>
<div className="relative z-10"> <div className="relative z-10">
<div ref={triggerRef} onClick={() => !readonly && toogleOption()} className={cn(readonly ? 'cursor-not-allowed' : 'cursor-pointer', 'flex items-center h-9 px-3 space-x-2 rounded-lg bg-gray-50 ')}> <div ref={triggerRef} onClick={() => !readonly && toogleOption()} className={cn(readonly ? 'cursor-not-allowed' : 'cursor-pointer', 'flex items-center h-9 px-3 space-x-2 rounded-lg bg-gray-50 ')}>
<ModelIcon modelId={currModel?.id as string} /> <ModelIcon modelId={currModel?.id as string} />
......
...@@ -480,7 +480,7 @@ const Main: FC<IMainProps> = () => { ...@@ -480,7 +480,7 @@ const Main: FC<IMainProps> = () => {
getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId), getChatList().filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
(draft) => { (draft) => {
if (!draft.find(item => item.id === questionId)) if (!draft.find(item => item.id === questionId))
draft.push({ ...questionItem }) draft.push({ ...questionItem } as any)
draft.push({ ...responseItem }) draft.push({ ...responseItem })
}) })
...@@ -506,9 +506,20 @@ const Main: FC<IMainProps> = () => { ...@@ -506,9 +506,20 @@ const Main: FC<IMainProps> = () => {
} }
}, },
onThought(thought) { onThought(thought) {
// debugger if (thought.thought === '[DONE]')
// thought then start to return message return
responseItem.id = thought.message_id;
// thought finished then start to return message
(responseItem as any).agent_thoughts.push(thought) (responseItem as any).agent_thoughts.push(thought)
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 })
})
// console.log('start render thought')
setChatList(newListWithAnswer)
}, },
onError() { onError() {
setResponsingFalse() setResponsingFalse()
......
'use client'
import { createContext, useContext } from 'use-context-selector'
import useSWR from 'swr'
import { fetchTenantInfo } from '@/service/common'
const ProviderContext = createContext<{ currentProvider: {
provider: string
provider_name: string
token_is_set: boolean
is_valid: boolean
token_is_valid: boolean
} | null | undefined }>({
currentProvider: null,
})
export const useProviderContext = () => useContext(ProviderContext)
type ProviderContextProviderProps = {
children: React.ReactNode
}
export const ProviderContextProvider = ({
children,
}: ProviderContextProviderProps) => {
const { data: userInfo } = useSWR({ url: '/info' }, fetchTenantInfo)
const currentProvider = userInfo?.providers?.find(({ token_is_set, is_valid }) => token_is_set && is_valid)
return (
<ProviderContext.Provider value={{ currentProvider }}>
{children}
</ProviderContext.Provider>
)
}
export default ProviderContext
...@@ -38,6 +38,7 @@ const translation = { ...@@ -38,6 +38,7 @@ const translation = {
universalChat: { universalChat: {
welcome: 'Start chat with Dify', welcome: 'Start chat with Dify',
welcomeDescribe: 'Your AI conversation companion for personalized assistance', welcomeDescribe: 'Your AI conversation companion for personalized assistance',
model: 'Model',
plugins: { plugins: {
name: 'Plugins', name: 'Plugins',
google_search: { google_search: {
......
...@@ -2,7 +2,7 @@ const translation = { ...@@ -2,7 +2,7 @@ const translation = {
title: '我的应用', title: '我的应用',
sidebar: { sidebar: {
discovery: '发现', discovery: '发现',
chat: '聊天', chat: '智聊',
workspace: '工作区', workspace: '工作区',
action: { action: {
pin: '置顶', pin: '置顶',
...@@ -38,7 +38,9 @@ const translation = { ...@@ -38,7 +38,9 @@ const translation = {
universalChat: { universalChat: {
welcome: '开始和 Dify 聊天吧', welcome: '开始和 Dify 聊天吧',
welcomeDescribe: '您的 AI 对话伴侣,为您提供个性化的帮助', welcomeDescribe: '您的 AI 对话伴侣,为您提供个性化的帮助',
model: '模型',
plugins: { plugins: {
name: '插件',
google_search: { google_search: {
name: '谷歌搜索', name: '谷歌搜索',
more: { more: {
......
/* eslint-disable no-new, prefer-promise-reject-errors */ /* eslint-disable no-new, prefer-promise-reject-errors */
import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config' import { API_PREFIX, IS_CE_EDITION, PUBLIC_API_PREFIX } from '@/config'
import Toast from '@/app/components/base/toast' import Toast from '@/app/components/base/toast'
import type { ThoughtItem } from '@/app/components/app/chat/type'
const TIME_OUT = 100000 const TIME_OUT = 100000
...@@ -30,7 +31,7 @@ export type IOnDataMoreInfo = { ...@@ -30,7 +31,7 @@ export type IOnDataMoreInfo = {
} }
export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void export type IOnData = (message: string, isFirstMessage: boolean, moreInfo: IOnDataMoreInfo) => void
export type IOnThought = (though: { id: string; tool: string; thought: string }) => void export type IOnThought = (though: ThoughtItem) => void
export type IOnCompleted = (hasError?: boolean) => void export type IOnCompleted = (hasError?: boolean) => void
export type IOnError = (msg: string) => void export type IOnError = (msg: string) => void
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment