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
927fac87
Commit
927fac87
authored
Jul 20, 2023
by
Joel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: show thought
parent
22966a36
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
101 additions
and
19 deletions
+101
-19
index.tsx
web/app/components/app/chat/answer/index.tsx
+27
-4
index.tsx
web/app/components/app/chat/index.tsx
+3
-1
index.tsx
web/app/components/app/chat/thought/index.tsx
+33
-0
style.module.css
web/app/components/app/chat/thought/style.module.css
+3
-0
type.ts
web/app/components/app/chat/type.ts
+6
-0
index.tsx
web/app/components/explore/universal-chat/index.tsx
+17
-5
base.ts
web/service/base.ts
+6
-4
share.ts
web/service/share.ts
+1
-1
universal-chat.ts
web/service/universal-chat.ts
+5
-4
No files found.
web/app/components/app/chat/answer/index.tsx
View file @
927fac87
...
...
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import
{
useContext
}
from
'use-context-selector'
import
{
UserCircleIcon
}
from
'@heroicons/react/24/solid'
import
cn
from
'classnames'
import
type
{
DisplayScene
,
FeedbackFunc
,
Feedbacktype
,
IChatItem
,
SubmitAnnotationFunc
}
from
'../type'
import
type
{
DisplayScene
,
FeedbackFunc
,
Feedbacktype
,
IChatItem
,
SubmitAnnotationFunc
,
ThoughtItem
}
from
'../type'
import
{
randomString
}
from
'../../../app-sidebar/basic'
import
OperationBtn
from
'../operation'
import
LoadingAnim
from
'../loading-anim'
...
...
@@ -13,13 +13,13 @@ import { EditIcon, EditIconSolid, OpeningStatementIcon, RatingIcon } from '../ic
import
s
from
'../style.module.css'
import
MoreInfo
from
'../more-info'
import
CopyBtn
from
'../copy-btn'
import
Thought
from
'../thought'
import
type
{
Annotation
,
MessageRating
}
from
'@/models/log'
import
AppContext
from
'@/context/app-context'
import
Tooltip
from
'@/app/components/base/tooltip'
import
{
Markdown
}
from
'@/app/components/base/markdown'
import
AutoHeightTextarea
from
'@/app/components/base/auto-height-textarea'
import
Button
from
'@/app/components/base/button'
const
Divider
:
FC
<
{
name
:
string
}
>
=
({
name
})
=>
{
const
{
t
}
=
useTranslation
()
return
<
div
className=
'flex items-center my-2'
>
...
...
@@ -43,9 +43,10 @@ export type IAnswerProps = {
displayScene
:
DisplayScene
isResponsing
?:
boolean
answerIconClassName
?:
string
thoughts
?:
ThoughtItem
[]
}
// 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
})
=>
{
const
Answer
:
FC
<
IAnswerProps
>
=
({
item
,
feedbackDisabled
=
false
,
isHideFeedbackEdit
=
false
,
onFeedback
,
onSubmitAnnotation
,
displayScene
=
'web'
,
isResponsing
,
answerIconClassName
,
thoughts
})
=>
{
const
{
id
,
content
,
more
,
feedback
,
adminFeedback
,
annotation
:
initAnnotation
}
=
item
const
[
showEdit
,
setShowEdit
]
=
useState
(
false
)
const
[
loading
,
setLoading
]
=
useState
(
false
)
...
...
@@ -54,6 +55,23 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
const
[
localAdminFeedback
,
setLocalAdminFeedback
]
=
useState
<
Feedbacktype
|
undefined
|
null
>
(
adminFeedback
)
const
{
userProfile
}
=
useContext
(
AppContext
)
const
{
t
}
=
useTranslation
()
// const thoughtList = [
// {
// id: '1',
// tool: 'google_search',
// thought: 'Searching Wikipedia with Donald Trump..',
// },
// {
// id: '2',
// tool: 'google_search',
// thought: 'Searching Wikipedia with Donald Trump..',
// },
// {
// id: '3',
// tool: 'google_search',
// thought: 'Searching Wikipedia with Donald Trump..',
// },
// ]
/**
* Render feedback results (distinguish between users and administrators)
...
...
@@ -184,7 +202,12 @@ const Answer: FC<IAnswerProps> = ({ item, feedbackDisabled = false, isHideFeedba
</
div
>
)
:
(
<
Markdown
content=
{
content
}
/>
<
div
>
{
(
thoughts
&&
thoughts
.
length
>
0
)
&&
(
<
Thought
list=
{
thoughts
||
[]
}
/>
)
}
<
Markdown
content=
{
content
}
/>
</
div
>
)
}
{
!
showEdit
?
(
annotation
?.
content
...
...
web/app/components/app/chat/index.tsx
View file @
927fac87
...
...
@@ -155,6 +155,7 @@ const Chat: FC<IChatProps> = ({
{
chatList
.
map
((
item
)
=>
{
if
(
item
.
isAnswer
)
{
const
isLast
=
item
.
id
===
chatList
[
chatList
.
length
-
1
].
id
const
thoughts
=
item
.
agent_thoughts
?.
filter
(
item
=>
item
.
thought
!==
'[DONE]'
)
return
<
Answer
key=
{
item
.
id
}
item=
{
item
}
...
...
@@ -165,6 +166,7 @@ const Chat: FC<IChatProps> = ({
displayScene=
{
displayScene
??
'web'
}
isResponsing=
{
isResponsing
&&
isLast
}
answerIconClassName=
{
answerIconClassName
}
thoughts=
{
thoughts
}
/>
}
return
<
Question
key=
{
item
.
id
}
id=
{
item
.
id
}
content=
{
item
.
content
}
more=
{
item
.
more
}
useCurrentUserAvatar=
{
useCurrentUserAvatar
}
/>
...
...
@@ -201,7 +203,7 @@ const Chat: FC<IChatProps> = ({
{
/* has scrollbar would hide part of first item */
}
<
div
ref=
{
suggestionListRef
}
className=
{
cn
(
!
hasScrollbar
&&
'justify-center'
,
'flex overflow-x-auto pb-2'
)
}
>
{
suggestionList
?.
map
((
item
,
index
)
=>
(
<
div
className=
'shrink-0 flex justify-center mr-2'
>
<
div
key=
{
item
}
className=
'shrink-0 flex justify-center mr-2'
>
<
Button
key=
{
index
}
onClick=
{
()
=>
setQuery
(
item
)
}
...
...
web/app/components/app/chat/thought/index.tsx
0 → 100644
View file @
927fac87
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
cn
from
'classnames'
import
type
{
ThoughtItem
}
from
'../type'
import
s
from
'./style.module.css'
export
type
IThoughtProps
=
{
list
:
ThoughtItem
[]
}
const
Thought
:
FC
<
IThoughtProps
>
=
({
list
,
})
=>
{
const
renderItem
=
(
item
:
ThoughtItem
)
=>
(
<
div
className=
'flex space-x-1'
key=
{
item
.
id
}
>
<
div
className=
'shrink-0'
>
{
item
.
tool
}
</
div
>
<
div
>
{
item
.
thought
}
</
div
>
</
div
>
)
// const [showMOre]
return
(
<
div
className=
{
cn
(
s
.
wrap
,
'mb-2 px-2 py-0.5 rounded-md'
)
}
>
<
div
className=
'flex items-center h-8 space-x-1'
>
<
div
>
Show the process of thinking
</
div
>
</
div
>
<
div
>
{
list
.
map
(
item
=>
renderItem
(
item
))
}
</
div
>
</
div
>
)
}
export
default
React
.
memo
(
Thought
)
web/app/components/app/chat/thought/style.module.css
0 → 100644
View file @
927fac87
.wrap
{
background-color
:
rgba
(
255
,
255
,
255
,
0.92
);
}
\ No newline at end of file
web/app/components/app/chat/type.ts
View file @
927fac87
...
...
@@ -16,9 +16,15 @@ export type SubmitAnnotationFunc = (messageId: string, content: string) => Promi
export
type
DisplayScene
=
'web'
|
'console'
export
type
ThoughtItem
=
{
id
:
string
tool
:
string
// plugin or dataset
thought
:
string
}
export
type
IChatItem
=
{
id
:
string
content
:
string
agent_thoughts
?:
ThoughtItem
[]
/**
* Specific message type
*/
...
...
web/app/components/explore/universal-chat/index.tsx
View file @
927fac87
...
...
@@ -27,7 +27,7 @@ import {
}
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'
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'
...
...
@@ -38,7 +38,6 @@ import type { DataSet } from '@/models/datasets'
import
ConfigSummary
from
'@/app/components/explore/universal-chat/config-view/summary'
import
ConfigDetail
from
'@/app/components/explore/universal-chat/config-view/detail'
import
{
fetchDatasets
}
from
'@/service/datasets'
const
APP_ID
=
'universal-chat'
const
DEFAULT_MODEL_ID
=
'claude-2'
// gpt-4, claude-2
const
DEFAULT_PLUGIN
=
{
...
...
@@ -177,7 +176,7 @@ const Main: FC<IMainProps> = () => {
if
(
!
isNewConversation
)
{
const
item
=
allConversationList
.
find
(
item
=>
item
.
id
===
currConversationId
)
as
any
notSyncToStateInputs
=
item
?.
inputs
||
{}
setCurrInputs
(
notSyncToStateInputs
)
//
setCurrInputs(notSyncToStateInputs)
notSyncToStateIntroduction
=
item
?.
introduction
||
''
setExistConversationInfo
({
name
:
item
?.
name
||
''
,
...
...
@@ -188,7 +187,7 @@ const Main: FC<IMainProps> = () => {
setModeId
(
modelConfig
.
model_id
)
const
pluginConfig
:
Record
<
string
,
boolean
>
=
{}
const
datasetIds
:
string
[]
=
[]
modelConfig
.
agent_mode
.
tools
.
forEach
((
item
)
=>
{
modelConfig
.
agent_mode
.
tools
.
forEach
((
item
:
any
)
=>
{
const
pluginName
=
Object
.
keys
(
item
)[
0
]
if
(
pluginName
===
'dataset'
)
datasetIds
.
push
(
item
.
dataset
.
id
)
...
...
@@ -228,6 +227,7 @@ const Main: FC<IMainProps> = () => {
isAnswer
:
false
,
})
newChatList
.
push
({
...
item
,
id
:
item
.
id
,
content
:
item
.
answer
,
feedback
:
item
.
feedback
,
...
...
@@ -272,6 +272,7 @@ const Main: FC<IMainProps> = () => {
if
(
chatListDomRef
.
current
)
chatListDomRef
.
current
.
scrollTop
=
chatListDomRef
.
current
.
scrollHeight
},
[
chatList
,
currConversationId
])
// user can not edit inputs if user had send message
const
canEditInpus
=
!
chatList
.
some
(
item
=>
item
.
isAnswer
===
false
)
&&
isNewConversation
const
createNewChat
=
async
()
=>
{
...
...
@@ -289,6 +290,9 @@ const Main: FC<IMainProps> = () => {
introduction
:
conversationIntroduction
,
})
}))
setModeId
(
DEFAULT_MODEL_ID
)
setPlugins
(
DEFAULT_PLUGIN
)
setDateSets
([])
}
// sometime introduction is not applied to state
...
...
@@ -435,6 +439,7 @@ const Main: FC<IMainProps> = () => {
const
questionItem
=
{
id
:
questionId
,
content
:
message
,
agent_thoughts
:
[],
isAnswer
:
false
,
}
...
...
@@ -449,9 +454,10 @@ const Main: FC<IMainProps> = () => {
setChatList
(
newList
)
// answer
const
responseItem
=
{
const
responseItem
:
IChatItem
=
{
id
:
`
${
Date
.
now
()}
`
,
content
:
''
,
agent_thoughts
:
[],
isAnswer
:
true
,
}
...
...
@@ -466,6 +472,7 @@ const Main: FC<IMainProps> = () => {
setAbortController
(
abortController
)
},
onData
:
(
message
:
string
,
isFirstMessage
:
boolean
,
{
conversationId
:
newConversationId
,
messageId
,
taskId
}:
any
)
=>
{
// console.log('get message...')
responseItem
.
content
=
responseItem
.
content
+
message
responseItem
.
id
=
messageId
if
(
isFirstMessage
&&
newConversationId
)
...
...
@@ -502,6 +509,11 @@ const Main: FC<IMainProps> = () => {
setIsShowSuggestion
(
true
)
}
},
onThought
(
thought
)
{
// thought then start to return message
// console.log('thought...');
(
responseItem
as
any
).
agent_thoughts
.
push
(
thought
)
},
onError
()
{
setResponsingFalse
()
// role back placeholder answer
...
...
web/service/base.ts
View file @
927fac87
...
...
@@ -30,6 +30,7 @@ export type IOnDataMoreInfo = {
}
export
type
IOnData
=
(
message
:
string
,
isFirstMessage
:
boolean
,
moreInfo
:
IOnDataMoreInfo
)
=>
void
export
type
IOnThought
=
(
though
:
{
id
:
string
;
tool
:
string
;
thought
:
string
})
=>
void
export
type
IOnCompleted
=
(
hasError
?:
boolean
)
=>
void
export
type
IOnError
=
(
msg
:
string
)
=>
void
...
...
@@ -39,6 +40,7 @@ type IOtherOptions = {
needAllResponseContent
?:
boolean
deleteContentType
?:
boolean
onData
?:
IOnData
// for stream
onThought
?:
IOnThought
onError
?:
IOnError
onCompleted
?:
IOnCompleted
// for stream
getAbortController
?:
(
abortController
:
AbortController
)
=>
void
...
...
@@ -61,7 +63,7 @@ export function format(text: string) {
return
res
.
replaceAll
(
'
\
n'
,
'<br/>'
).
replaceAll
(
'```'
,
''
)
}
const
handleStream
=
(
response
:
any
,
onData
:
IOnData
,
onCompleted
?:
IOnCompleted
)
=>
{
const
handleStream
=
(
response
:
any
,
onData
:
IOnData
,
onCompleted
?:
IOnCompleted
,
onThought
?:
IOnThought
)
=>
{
if
(
!
response
.
ok
)
throw
new
Error
(
'Network response was not ok'
)
...
...
@@ -114,7 +116,7 @@ const handleStream = (response: any, onData: IOnData, onCompleted?: IOnCompleted
isFirstMessage
=
false
}
else
if
(
bufferObj
.
event
===
'agent_thought'
)
{
console
.
log
(
bufferObj
)
onThought
?.(
bufferObj
as
any
)
}
}
})
...
...
@@ -306,7 +308,7 @@ export const upload = (options: any): Promise<any> => {
})
}
export
const
ssePost
=
(
url
:
string
,
fetchOptions
:
any
,
{
isPublicAPI
=
false
,
onData
,
onCompleted
,
onError
,
getAbortController
}:
IOtherOptions
)
=>
{
export
const
ssePost
=
(
url
:
string
,
fetchOptions
:
any
,
{
isPublicAPI
=
false
,
onData
,
onCompleted
,
on
Thought
,
on
Error
,
getAbortController
}:
IOtherOptions
)
=>
{
const
abortController
=
new
AbortController
()
const
options
=
Object
.
assign
({},
baseOptions
,
{
...
...
@@ -348,7 +350,7 @@ export const ssePost = (url: string, fetchOptions: any, { isPublicAPI = false, o
return
}
onData
?.(
str
,
isFirstMessage
,
moreInfo
)
},
onCompleted
)
},
onCompleted
,
onThought
)
}).
catch
((
e
)
=>
{
if
(
e
.
toString
()
!==
'AbortError: The user aborted a request.'
)
Toast
.
notify
({
type
:
'error'
,
message
:
e
})
...
...
web/service/share.ts
View file @
927fac87
...
...
@@ -3,7 +3,7 @@ import {
del
as
consoleDel
,
get
as
consoleGet
,
patch
as
consolePatch
,
post
as
consolePost
,
delPublic
as
del
,
getPublic
as
get
,
patchPublic
as
patch
,
postPublic
as
post
,
ssePost
,
}
from
'./base'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat
/type
'
function
getAction
(
action
:
'get'
|
'post'
|
'del'
|
'patch'
,
isInstalledApp
:
boolean
)
{
switch
(
action
)
{
...
...
web/service/universal-chat.ts
View file @
927fac87
import
type
{
IOnCompleted
,
IOnData
,
IOnError
}
from
'./base'
import
type
{
IOnCompleted
,
IOnData
,
IOnError
,
IOnThought
}
from
'./base'
import
{
del
,
get
,
patch
,
post
,
ssePost
,
}
from
'./base'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat'
import
type
{
Feedbacktype
}
from
'@/app/components/app/chat
/type
'
const
baseUrl
=
'universal-chat'
...
...
@@ -10,10 +10,11 @@ function getUrl(url: string) {
return
`
${
baseUrl
}
/
${
url
.
startsWith
(
'/'
)
?
url
.
slice
(
1
)
:
url
}
`
}
export
const
sendChatMessage
=
async
(
body
:
Record
<
string
,
any
>
,
{
onData
,
onCompleted
,
onError
,
getAbortController
}:
{
export
const
sendChatMessage
=
async
(
body
:
Record
<
string
,
any
>
,
{
onData
,
onCompleted
,
onError
,
onThought
,
getAbortController
}:
{
onData
:
IOnData
onCompleted
:
IOnCompleted
onError
:
IOnError
onThought
:
IOnThought
getAbortController
?:
(
abortController
:
AbortController
)
=>
void
})
=>
{
return
ssePost
(
getUrl
(
'messages'
),
{
...
...
@@ -21,7 +22,7 @@ export const sendChatMessage = async (body: Record<string, any>, { onData, onCom
...
body
,
response_mode
:
'streaming'
,
},
},
{
onData
,
onCompleted
,
onError
,
getAbortController
})
},
{
onData
,
onCompleted
,
on
Thought
,
on
Error
,
getAbortController
})
}
export
const
stopChatMessageResponding
=
async
(
appId
:
string
,
taskId
:
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