Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
D
dify
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tech
dify
Commits
bec998ab
Unverified
Commit
bec998ab
authored
Jan 25, 2024
by
Joel
Committed by
GitHub
Jan 25, 2024
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chore: remove universal chat code (#2194)
parent
77636945
Changes
26
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
10 additions
and
1720 deletions
+10
-1720
page.tsx
web/app/(commonLayout)/explore/chat/page.tsx
+0
-13
index.tsx
...ents/app/configuration/dataset-config/card-item/index.tsx
+0
-1
index.tsx
web/app/components/base/audio-btn/index.tsx
+1
-1
index.tsx
web/app/components/base/voice-input/index.tsx
+1
-1
index.tsx
...nents/explore/universal-chat/config-view/detail/index.tsx
+0
-38
style.module.css
...xplore/universal-chat/config-view/detail/style.module.css
+0
-9
index.tsx
...ents/explore/universal-chat/config-view/summary/index.tsx
+0
-107
style.module.css
...plore/universal-chat/config-view/summary/style.module.css
+0
-21
index.tsx
...nents/explore/universal-chat/config/data-config/index.tsx
+0
-94
index.tsx
web/app/components/explore/universal-chat/config/index.tsx
+0
-55
index.tsx
...ents/explore/universal-chat/config/model-config/index.tsx
+0
-39
index.tsx
...ts/explore/universal-chat/config/plugins-config/index.tsx
+0
-103
item.module.css
...lore/universal-chat/config/plugins-config/item.module.css
+0
-3
item.tsx
...nts/explore/universal-chat/config/plugins-config/item.tsx
+0
-43
use-conversation.ts
...mponents/explore/universal-chat/hooks/use-conversation.ts
+0
-73
index.tsx
web/app/components/explore/universal-chat/index.tsx
+0
-831
index.tsx
web/app/components/explore/universal-chat/init/index.tsx
+0
-43
style.module.css
...p/components/explore/universal-chat/init/style.module.css
+0
-9
index.tsx
web/app/components/share/chat/index.tsx
+0
-1
index.tsx
web/app/components/share/chat/sidebar/index.tsx
+6
-19
index.tsx
web/app/components/share/chat/sidebar/list/index.tsx
+2
-13
index.ts
web/config/index.ts
+0
-2
explore.en.ts
web/i18n/lang/explore.en.ts
+0
-39
explore.pt.ts
web/i18n/lang/explore.pt.ts
+0
-39
explore.zh.ts
web/i18n/lang/explore.zh.ts
+0
-39
universal-chat.ts
web/service/universal-chat.ts
+0
-84
No files found.
web/app/(commonLayout)/explore/chat/page.tsx
deleted
100644 → 0
View file @
77636945
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
)
web/app/components/app/configuration/dataset-config/card-item/index.tsx
View file @
bec998ab
...
@@ -16,7 +16,6 @@ export type ICardItemProps = {
...
@@ -16,7 +16,6 @@ export type ICardItemProps = {
onRemove
:
(
id
:
string
)
=>
void
onRemove
:
(
id
:
string
)
=>
void
readonly
?:
boolean
readonly
?:
boolean
}
}
// used in universal-chat
const
CardItem
:
FC
<
ICardItemProps
>
=
({
const
CardItem
:
FC
<
ICardItemProps
>
=
({
className
,
className
,
config
,
config
,
...
...
web/app/components/base/audio-btn/index.tsx
View file @
bec998ab
...
@@ -33,7 +33,7 @@ const AudioBtn = ({
...
@@ -33,7 +33,7 @@ const AudioBtn = ({
if
(
value
!==
''
)
{
if
(
value
!==
''
)
{
formData
.
append
(
'text'
,
removeCodeBlocks
(
value
))
formData
.
append
(
'text'
,
removeCodeBlocks
(
value
))
let
url
=
'
/universal-chat/text-to-audio
'
let
url
=
''
let
isPublic
=
false
let
isPublic
=
false
if
(
params
.
token
)
{
if
(
params
.
token
)
{
...
...
web/app/components/base/voice-input/index.tsx
View file @
bec998ab
...
@@ -86,7 +86,7 @@ const VoiceInput = ({
...
@@ -86,7 +86,7 @@ const VoiceInput = ({
const
formData
=
new
FormData
()
const
formData
=
new
FormData
()
formData
.
append
(
'file'
,
mp3File
)
formData
.
append
(
'file'
,
mp3File
)
let
url
=
'
/universal-chat/audio-to-text
'
let
url
=
''
let
isPublic
=
false
let
isPublic
=
false
if
(
params
.
token
)
{
if
(
params
.
token
)
{
...
...
web/app/components/explore/universal-chat/config-view/detail/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config-view/detail/style.module.css
deleted
100644 → 0
View file @
77636945
.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
);
}
web/app/components/explore/universal-chat/config-view/summary/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config-view/summary/style.module.css
deleted
100644 → 0
View file @
77636945
.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
)
}
\ No newline at end of file
web/app/components/explore/universal-chat/config/data-config/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config/model-config/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config/plugins-config/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/config/plugins-config/item.module.css
deleted
100644 → 0
View file @
77636945
.shadow
{
box-shadow
:
0px
1px
2px
0px
rgba
(
16
,
24
,
40
,
0.05
);
}
\ No newline at end of file
web/app/components/explore/universal-chat/config/plugins-config/item.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/hooks/use-conversation.ts
deleted
100644 → 0
View file @
77636945
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
web/app/components/explore/universal-chat/index.tsx
deleted
100644 → 0
View file @
77636945
/* 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
)
web/app/components/explore/universal-chat/init/index.tsx
deleted
100644 → 0
View file @
77636945
'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
)
web/app/components/explore/universal-chat/init/style.module.css
deleted
100644 → 0
View file @
77636945
.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
;
}
web/app/components/share/chat/index.tsx
View file @
bec998ab
...
@@ -58,7 +58,6 @@ export type IMainProps = {
...
@@ -58,7 +58,6 @@ export type IMainProps = {
isInstalledApp
?:
boolean
isInstalledApp
?:
boolean
installedAppInfo
?:
InstalledApp
installedAppInfo
?:
InstalledApp
isSupportPlugin
?:
boolean
isSupportPlugin
?:
boolean
isUniversalChat
?:
boolean
}
}
const
Main
:
FC
<
IMainProps
>
=
({
const
Main
:
FC
<
IMainProps
>
=
({
...
...
web/app/components/share/chat/sidebar/index.tsx
View file @
bec998ab
...
@@ -11,7 +11,6 @@ import AppInfo from '@/app/components/share/chat/sidebar/app-info'
...
@@ -11,7 +11,6 @@ import AppInfo from '@/app/components/share/chat/sidebar/app-info'
// import Card from './card'
// import Card from './card'
import
type
{
ConversationItem
,
SiteInfo
}
from
'@/models/share'
import
type
{
ConversationItem
,
SiteInfo
}
from
'@/models/share'
import
{
fetchConversations
}
from
'@/service/share'
import
{
fetchConversations
}
from
'@/service/share'
import
{
fetchConversations
as
fetchUniversalConversations
}
from
'@/service/universal-chat'
export
type
ISidebarProps
=
{
export
type
ISidebarProps
=
{
copyRight
:
string
copyRight
:
string
...
@@ -25,7 +24,6 @@ export type ISidebarProps = {
...
@@ -25,7 +24,6 @@ export type ISidebarProps = {
isClearPinnedConversationList
:
boolean
isClearPinnedConversationList
:
boolean
isInstalledApp
:
boolean
isInstalledApp
:
boolean
installedAppId
?:
string
installedAppId
?:
string
isUniversalChat
?:
boolean
siteInfo
:
SiteInfo
siteInfo
:
SiteInfo
onMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
onMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
onPinnedMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
onPinnedMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
...
@@ -50,7 +48,6 @@ const Sidebar: FC<ISidebarProps> = ({
...
@@ -50,7 +48,6 @@ const Sidebar: FC<ISidebarProps> = ({
isClearPinnedConversationList
,
isClearPinnedConversationList
,
isInstalledApp
,
isInstalledApp
,
installedAppId
,
installedAppId
,
isUniversalChat
,
siteInfo
,
siteInfo
,
onMoreLoaded
,
onMoreLoaded
,
onPinnedMoreLoaded
,
onPinnedMoreLoaded
,
...
@@ -66,13 +63,7 @@ const Sidebar: FC<ISidebarProps> = ({
...
@@ -66,13 +63,7 @@ const Sidebar: FC<ISidebarProps> = ({
const
[
hasPinned
,
setHasPinned
]
=
useState
(
false
)
const
[
hasPinned
,
setHasPinned
]
=
useState
(
false
)
const
checkHasPinned
=
async
()
=>
{
const
checkHasPinned
=
async
()
=>
{
let
res
:
any
const
res
=
await
fetchConversations
(
isInstalledApp
,
installedAppId
,
undefined
,
true
)
as
any
if
(
isUniversalChat
)
res
=
await
fetchUniversalConversations
(
undefined
,
true
)
else
res
=
await
fetchConversations
(
isInstalledApp
,
installedAppId
,
undefined
,
true
)
setHasPinned
(
res
.
data
.
length
>
0
)
setHasPinned
(
res
.
data
.
length
>
0
)
}
}
...
@@ -85,13 +76,13 @@ const Sidebar: FC<ISidebarProps> = ({
...
@@ -85,13 +76,13 @@ const Sidebar: FC<ISidebarProps> = ({
checkHasPinned
()
checkHasPinned
()
},
[
controlUpdateList
])
},
[
controlUpdateList
])
const
maxListHeight
=
(
isInstalledApp
||
isUniversalChat
)
?
'max-h-[30vh]'
:
'max-h-[40vh]'
const
maxListHeight
=
(
isInstalledApp
)
?
'max-h-[30vh]'
:
'max-h-[40vh]'
return
(
return
(
<
div
<
div
className=
{
className=
{
cn
(
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'
,
'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> = ({
...
@@ -125,7 +116,6 @@ const Sidebar: FC<ISidebarProps> = ({
isClearConversationList=
{
isClearPinnedConversationList
}
isClearConversationList=
{
isClearPinnedConversationList
}
isInstalledApp=
{
isInstalledApp
}
isInstalledApp=
{
isInstalledApp
}
installedAppId=
{
installedAppId
}
installedAppId=
{
installedAppId
}
isUniversalChat=
{
isUniversalChat
}
onMoreLoaded=
{
onPinnedMoreLoaded
}
onMoreLoaded=
{
onPinnedMoreLoaded
}
isNoMore=
{
isPinnedNoMore
}
isNoMore=
{
isPinnedNoMore
}
isPinned=
{
true
}
isPinned=
{
true
}
...
@@ -149,7 +139,6 @@ const Sidebar: FC<ISidebarProps> = ({
...
@@ -149,7 +139,6 @@ const Sidebar: FC<ISidebarProps> = ({
isClearConversationList=
{
isClearConversationList
}
isClearConversationList=
{
isClearConversationList
}
isInstalledApp=
{
isInstalledApp
}
isInstalledApp=
{
isInstalledApp
}
installedAppId=
{
installedAppId
}
installedAppId=
{
installedAppId
}
isUniversalChat=
{
isUniversalChat
}
onMoreLoaded=
{
onMoreLoaded
}
onMoreLoaded=
{
onMoreLoaded
}
isNoMore=
{
isNoMore
}
isNoMore=
{
isNoMore
}
isPinned=
{
false
}
isPinned=
{
false
}
...
@@ -160,11 +149,9 @@ const Sidebar: FC<ISidebarProps> = ({
...
@@ -160,11 +149,9 @@ const Sidebar: FC<ISidebarProps> = ({
</
div
>
</
div
>
</
div
>
</
div
>
{
!
isUniversalChat
&&
(
<
div
className=
"flex flex-shrink-0 pr-4 pb-4 pl-4"
>
<
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
className=
"text-gray-400 font-normal text-xs"
>
©
{
copyRight
}
{
(
new
Date
()).
getFullYear
()
}
</
div
>
</
div
>
</
div
>
)
}
</
div
>
</
div
>
)
)
}
}
...
...
web/app/components/share/chat/sidebar/list/index.tsx
View file @
bec998ab
...
@@ -9,7 +9,6 @@ import RenameModal from '../rename-modal'
...
@@ -9,7 +9,6 @@ import RenameModal from '../rename-modal'
import
Item
from
'./item'
import
Item
from
'./item'
import
type
{
ConversationItem
}
from
'@/models/share'
import
type
{
ConversationItem
}
from
'@/models/share'
import
{
fetchConversations
,
renameConversation
}
from
'@/service/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'
import
Toast
from
'@/app/components/base/toast'
export
type
IListProps
=
{
export
type
IListProps
=
{
...
@@ -20,7 +19,6 @@ export type IListProps = {
...
@@ -20,7 +19,6 @@ export type IListProps = {
onListChanged
?:
(
newList
:
ConversationItem
[])
=>
void
onListChanged
?:
(
newList
:
ConversationItem
[])
=>
void
isClearConversationList
:
boolean
isClearConversationList
:
boolean
isInstalledApp
:
boolean
isInstalledApp
:
boolean
isUniversalChat
?:
boolean
installedAppId
?:
string
installedAppId
?:
string
onMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
onMoreLoaded
:
(
res
:
{
data
:
ConversationItem
[];
has_more
:
boolean
})
=>
void
isNoMore
:
boolean
isNoMore
:
boolean
...
@@ -38,7 +36,6 @@ const List: FC<IListProps> = ({
...
@@ -38,7 +36,6 @@ const List: FC<IListProps> = ({
onListChanged
,
onListChanged
,
isClearConversationList
,
isClearConversationList
,
isInstalledApp
,
isInstalledApp
,
isUniversalChat
,
installedAppId
,
installedAppId
,
onMoreLoaded
,
onMoreLoaded
,
isNoMore
,
isNoMore
,
...
@@ -56,11 +53,7 @@ const List: FC<IListProps> = ({
...
@@ -56,11 +53,7 @@ const List: FC<IListProps> = ({
let
lastId
=
!
isClearConversationList
?
list
[
list
.
length
-
1
]?.
id
:
undefined
let
lastId
=
!
isClearConversationList
?
list
[
list
.
length
-
1
]?.
id
:
undefined
if
(
lastId
===
'-1'
)
if
(
lastId
===
'-1'
)
lastId
=
undefined
lastId
=
undefined
let
res
:
any
const
res
=
await
fetchConversations
(
isInstalledApp
,
installedAppId
,
lastId
,
isPinned
)
as
any
if
(
isUniversalChat
)
res
=
await
fetchUniversalConversations
(
lastId
,
isPinned
)
else
res
=
await
fetchConversations
(
isInstalledApp
,
installedAppId
,
lastId
,
isPinned
)
const
{
data
:
conversations
,
has_more
}:
any
=
res
const
{
data
:
conversations
,
has_more
}:
any
=
res
onMoreLoaded
({
data
:
conversations
,
has_more
})
onMoreLoaded
({
data
:
conversations
,
has_more
})
}
}
...
@@ -93,10 +86,6 @@ const List: FC<IListProps> = ({
...
@@ -93,10 +86,6 @@ const List: FC<IListProps> = ({
setIsSaving
()
setIsSaving
()
const
currId
=
currentConversation
.
id
const
currId
=
currentConversation
.
id
try
{
try
{
if
(
isUniversalChat
)
await
renameUniversalConversation
(
currId
,
newName
)
else
await
renameConversation
(
isInstalledApp
,
installedAppId
,
currId
,
newName
)
await
renameConversation
(
isInstalledApp
,
installedAppId
,
currId
,
newName
)
Toast
.
notify
({
Toast
.
notify
({
...
...
web/config/index.ts
View file @
bec998ab
...
@@ -52,8 +52,6 @@ export const MODEL_LIST = [
...
@@ -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-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
{
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
=
[
export
const
TONE_LIST
=
[
{
{
id
:
1
,
id
:
1
,
...
...
web/i18n/lang/explore.en.ts
View file @
bec998ab
...
@@ -36,45 +36,6 @@ const translation = {
...
@@ -36,45 +36,6 @@ const translation = {
Programming
:
'Programming'
,
Programming
:
'Programming'
,
HR
:
'HR'
,
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
export
default
translation
web/i18n/lang/explore.pt.ts
View file @
bec998ab
...
@@ -36,45 +36,6 @@ const translation = {
...
@@ -36,45 +36,6 @@ const translation = {
Programming
:
'Programação'
,
Programming
:
'Programação'
,
HR
:
'RH'
,
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
export
default
translation
web/i18n/lang/explore.zh.ts
View file @
bec998ab
...
@@ -36,45 +36,6 @@ const translation = {
...
@@ -36,45 +36,6 @@ const translation = {
Programming
:
'编程'
,
Programming
:
'编程'
,
HR
:
'人力资源'
,
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
export
default
translation
web/service/universal-chat.ts
deleted
100644 → 0
View file @
77636945
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
}
>
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment