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
bd52937c
Commit
bd52937c
authored
Mar 11, 2024
by
StyleZhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
chat workflow run
parent
7655d7f6
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
257 additions
and
18 deletions
+257
-18
run-and-history.tsx
web/app/components/workflow/header/run-and-history.tsx
+3
-4
hooks.ts
web/app/components/workflow/hooks.ts
+6
-2
chat-wrapper.tsx
...ponents/workflow/panel/debug-and-preview/chat-wrapper.tsx
+25
-7
hooks.ts
web/app/components/workflow/panel/debug-and-preview/hooks.ts
+192
-0
user-input.tsx
...omponents/workflow/panel/debug-and-preview/user-input.tsx
+30
-4
base.ts
web/service/base.ts
+1
-1
No files found.
web/app/components/workflow/header/run-and-history.tsx
View file @
bd52937c
...
...
@@ -56,7 +56,6 @@ const PreviewMode = memo(() => {
const
{
t
}
=
useTranslation
()
const
{
handleRunInit
}
=
useWorkflow
()
const
runningStatus
=
useStore
(
s
=>
s
.
runningStatus
)
const
isRunning
=
runningStatus
===
WorkflowRunningStatus
.
Running
const
handleClick
=
()
=>
{
handleRunInit
()
...
...
@@ -67,12 +66,12 @@ const PreviewMode = memo(() => {
className=
{
`
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
hover:bg-primary-50 cursor-pointer
${
isRunning
&& 'bg-primary-50 opacity-50 !cursor-not-allowed'}
${
runningStatus
&& 'bg-primary-50 opacity-50 !cursor-not-allowed'}
`
}
onClick=
{
()
=>
!
isRunning
&&
handleClick
()
}
onClick=
{
()
=>
!
runningStatus
&&
handleClick
()
}
>
{
isRunning
runningStatus
?
(
<>
{
t
(
'workflow.common.inPreview'
)
}
...
...
web/app/components/workflow/hooks.ts
View file @
bd52937c
...
...
@@ -36,6 +36,7 @@ import { syncWorkflowDraft } from '@/service/workflow'
import
{
useFeaturesStore
}
from
'@/app/components/base/features/hooks'
import
{
useStore
as
useAppStore
}
from
'@/app/components/app/store'
import
{
ssePost
}
from
'@/service/base'
import
type
{
IOtherOptions
}
from
'@/service/base'
export
const
useIsChatMode
=
()
=>
{
const
appDetail
=
useAppStore
(
s
=>
s
.
appDetail
)
...
...
@@ -671,7 +672,7 @@ export const useWorkflow = () => {
export
const
useWorkflowRun
=
()
=>
{
const
store
=
useStoreApi
()
return
(
params
:
any
)
=>
{
const
run
=
useCallback
((
params
:
any
,
callback
?:
IOtherOptions
)
=>
{
const
{
getNodes
,
setNodes
,
...
...
@@ -721,7 +722,10 @@ export const useWorkflowRun = () => {
})
setNodes
(
newNodes
)
},
...
callback
,
},
)
}
},
[
store
])
return
run
}
web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx
View file @
bd52937c
import
{
memo
,
useCallback
,
}
from
'react'
import
{
useStore
}
from
'../../store'
import
UserInput
from
'./user-input'
import
{
useChat
}
from
'./hooks'
import
Chat
from
'@/app/components/base/chat/chat'
import
{
useChat
}
from
'@/app/components/base/chat/chat/hook
s'
import
type
{
OnSend
}
from
'@/app/components/base/chat/type
s'
const
ChatWrapper
=
()
=>
{
const
{
conversationId
,
chatList
,
handleStop
,
isResponding
,
suggestedQuestions
,
handleSend
,
}
=
useChat
()
const
doSend
=
useCallback
<
OnSend
>
((
query
,
files
)
=>
{
handleSend
({
query
,
files
,
inputs
:
useStore
.
getState
().
inputs
,
conversationId
,
})
},
[
conversationId
,
handleSend
])
return
(
<
Chat
chatList=
{
[]
}
chatList=
{
chatList
}
isResponding=
{
isResponding
}
chatContainerclassName=
'px-4'
chatContainerInnerClassName=
'p
x-4
'
chatFooterClassName=
'p
b
-4'
chatFooterInnerClassName=
'p
x
-4'
onSend=
{
()
=>
{}
}
chatContainerInnerClassName=
'p
t-6
'
chatFooterClassName=
'p
x
-4'
chatFooterInnerClassName=
'p
b
-4'
onSend=
{
doSend
}
onStopResponding=
{
handleStop
}
chatNode=
{
<
UserInput
/>
}
allToolIcons=
{
{}
}
...
...
@@ -26,4 +44,4 @@ const ChatWrapper = () => {
)
}
export
default
ChatWrapper
export
default
memo
(
ChatWrapper
)
web/app/components/workflow/panel/debug-and-preview/hooks.ts
0 → 100644
View file @
bd52937c
import
{
useCallback
,
useEffect
,
useRef
,
useState
,
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
produce
,
setAutoFreeze
}
from
'immer'
import
{
useWorkflowRun
}
from
'../../hooks'
import
type
{
ChatItem
}
from
'@/app/components/base/chat/types'
import
{
useToastContext
}
from
'@/app/components/base/toast'
import
{
TransferMethod
}
from
'@/types/app'
import
type
{
VisionFile
}
from
'@/types/app'
export
const
useChat
=
(
prevChatList
?:
ChatItem
[],
)
=>
{
const
{
t
}
=
useTranslation
()
const
{
notify
}
=
useToastContext
()
const
run
=
useWorkflowRun
()
const
hasStopResponded
=
useRef
(
false
)
const
connversationId
=
useRef
(
''
)
const
taskIdRef
=
useRef
(
''
)
const
[
chatList
,
setChatList
]
=
useState
<
ChatItem
[]
>
(
prevChatList
||
[])
const
chatListRef
=
useRef
<
ChatItem
[]
>
(
prevChatList
||
[])
const
[
isResponding
,
setIsResponding
]
=
useState
(
false
)
const
isRespondingRef
=
useRef
(
false
)
const
[
suggestedQuestions
,
setSuggestQuestions
]
=
useState
<
string
[]
>
([])
const
stopAbortControllerRef
=
useRef
<
AbortController
|
null
>
(
null
)
const
suggestedQuestionsAbortControllerRef
=
useRef
<
AbortController
|
null
>
(
null
)
useEffect
(()
=>
{
setAutoFreeze
(
false
)
return
()
=>
{
setAutoFreeze
(
true
)
}
},
[])
const
handleUpdateChatList
=
useCallback
((
newChatList
:
ChatItem
[])
=>
{
setChatList
(
newChatList
)
chatListRef
.
current
=
newChatList
},
[])
const
handleResponding
=
useCallback
((
isResponding
:
boolean
)
=>
{
setIsResponding
(
isResponding
)
isRespondingRef
.
current
=
isResponding
},
[])
const
handleStop
=
useCallback
(()
=>
{
hasStopResponded
.
current
=
true
handleResponding
(
false
)
},
[
handleResponding
])
const
updateCurrentQA
=
useCallback
(({
responseItem
,
questionId
,
placeholderAnswerId
,
questionItem
,
}:
{
responseItem
:
ChatItem
questionId
:
string
placeholderAnswerId
:
string
questionItem
:
ChatItem
})
=>
{
const
newListWithAnswer
=
produce
(
chatListRef
.
current
.
filter
(
item
=>
item
.
id
!==
responseItem
.
id
&&
item
.
id
!==
placeholderAnswerId
),
(
draft
)
=>
{
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
draft
.
push
({
...
questionItem
})
draft
.
push
({
...
responseItem
})
})
handleUpdateChatList
(
newListWithAnswer
)
},
[
handleUpdateChatList
])
const
handleSend
=
useCallback
((
params
:
any
)
=>
{
if
(
isRespondingRef
.
current
)
{
notify
({
type
:
'info'
,
message
:
t
(
'appDebug.errorMessage.waitForResponse'
)
})
return
false
}
const
questionId
=
`question-
${
Date
.
now
()}
`
const
questionItem
=
{
id
:
questionId
,
content
:
params
.
query
,
isAnswer
:
false
,
message_files
:
params
.
files
,
}
const
placeholderAnswerId
=
`answer-placeholder-
${
Date
.
now
()}
`
const
placeholderAnswerItem
=
{
id
:
placeholderAnswerId
,
content
:
''
,
isAnswer
:
true
,
}
const
newList
=
[...
chatListRef
.
current
,
questionItem
,
placeholderAnswerItem
]
handleUpdateChatList
(
newList
)
// answer
const
responseItem
:
ChatItem
=
{
id
:
`
${
Date
.
now
()}
`
,
content
:
''
,
agent_thoughts
:
[],
message_files
:
[],
isAnswer
:
true
,
}
handleResponding
(
true
)
const
bodyParams
=
{
conversation_id
:
connversationId
.
current
,
...
params
,
}
if
(
bodyParams
?.
files
?.
length
)
{
bodyParams
.
files
=
bodyParams
.
files
.
map
((
item
:
VisionFile
)
=>
{
if
(
item
.
transfer_method
===
TransferMethod
.
local_file
)
{
return
{
...
item
,
url
:
''
,
}
}
return
item
})
}
let
hasSetResponseId
=
false
run
(
params
,
{
onData
:
(
message
:
string
,
isFirstMessage
:
boolean
,
{
conversationId
:
newConversationId
,
messageId
,
taskId
}:
any
)
=>
{
responseItem
.
content
=
responseItem
.
content
+
message
if
(
messageId
&&
!
hasSetResponseId
)
{
responseItem
.
id
=
messageId
hasSetResponseId
=
true
}
if
(
isFirstMessage
&&
newConversationId
)
connversationId
.
current
=
newConversationId
taskIdRef
.
current
=
taskId
if
(
messageId
)
responseItem
.
id
=
messageId
updateCurrentQA
({
responseItem
,
questionId
,
placeholderAnswerId
,
questionItem
,
})
},
async
onCompleted
(
hasError
?:
boolean
)
{
handleResponding
(
false
)
},
onMessageEnd
:
(
messageEnd
)
=>
{
responseItem
.
citation
=
messageEnd
.
metadata
?.
retriever_resources
||
[]
const
newListWithAnswer
=
produce
(
chatListRef
.
current
.
filter
(
item
=>
item
.
id
!==
responseItem
.
id
&&
item
.
id
!==
placeholderAnswerId
),
(
draft
)
=>
{
if
(
!
draft
.
find
(
item
=>
item
.
id
===
questionId
))
draft
.
push
({
...
questionItem
})
draft
.
push
({
...
responseItem
})
})
handleUpdateChatList
(
newListWithAnswer
)
},
onMessageReplace
:
(
messageReplace
)
=>
{
responseItem
.
content
=
messageReplace
.
answer
},
onError
()
{
handleResponding
(
false
)
const
newChatList
=
produce
(
chatListRef
.
current
,
(
draft
)
=>
{
draft
.
splice
(
draft
.
findIndex
(
item
=>
item
.
id
===
placeholderAnswerId
),
1
)
})
handleUpdateChatList
(
newChatList
)
},
},
)
},
[
run
,
handleResponding
,
handleUpdateChatList
,
notify
,
t
,
updateCurrentQA
])
return
{
conversationId
:
connversationId
.
current
,
chatList
,
handleSend
,
handleStop
,
isResponding
,
suggestedQuestions
,
}
}
web/app/components/workflow/panel/debug-and-preview/user-input.tsx
View file @
bd52937c
...
...
@@ -3,11 +3,27 @@ import {
useState
,
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useNodes
}
from
'reactflow'
import
FormItem
from
'../../nodes/_base/components/before-run-form/form-item'
import
{
BlockEnum
}
from
'../../types'
import
{
useStore
}
from
'../../store'
import
type
{
StartNodeType
}
from
'../../nodes/start/types'
import
{
ChevronDown
}
from
'@/app/components/base/icons/src/vender/line/arrows'
const
UserInput
=
()
=>
{
const
{
t
}
=
useTranslation
()
const
[
expanded
,
setExpanded
]
=
useState
(
true
)
const
inputs
=
useStore
(
s
=>
s
.
inputs
)
const
nodes
=
useNodes
<
StartNodeType
>
()
const
startNode
=
nodes
.
find
(
node
=>
node
.
data
.
type
===
BlockEnum
.
Start
)
const
variables
=
startNode
?.
data
.
variables
||
[]
const
handleValueChange
=
(
variable
:
string
,
v
:
string
)
=>
{
useStore
.
getState
().
setInputs
({
...
inputs
,
[
variable
]:
v
,
})
}
return
(
<
div
...
...
@@ -32,10 +48,20 @@ const UserInput = () => {
{
expanded
&&
(
<
div
className=
'py-2 text-[13px] text-gray-900'
>
<
div
className=
'flex px-4 py-1'
>
<
div
className=
'shrink-0 mr-4 leading-8'
>
Service Name
</
div
>
<
input
className=
'grow px-3 h-8 appearance-none outline-none rounded-lg bg-gray-100'
/>
</
div
>
{
variables
.
map
(
variable
=>
(
<
div
key=
{
variable
.
variable
}
className=
'mb-2 last-of-type:mb-0'
>
<
FormItem
payload=
{
variable
}
value=
{
inputs
[
variable
.
variable
]
}
onChange=
{
v
=>
handleValueChange
(
variable
.
variable
,
v
)
}
/>
</
div
>
))
}
</
div
>
)
}
...
...
web/service/base.ts
View file @
bd52937c
...
...
@@ -54,7 +54,7 @@ export type IOnNodeFinished = (nodeFinished: NodeFinishedResponse) => void
export
type
IOnTextChunk
=
(
textChunk
:
TextChunkResponse
)
=>
void
export
type
IOnTextReplace
=
(
textReplace
:
TextReplaceResponse
)
=>
void
type
IOtherOptions
=
{
export
type
IOtherOptions
=
{
isPublicAPI
?:
boolean
bodyStringify
?:
boolean
needAllResponseContent
?:
boolean
...
...
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