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
7655d7f6
Commit
7655d7f6
authored
Mar 11, 2024
by
StyleZhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
run
parent
84e2071a
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
145 additions
and
27 deletions
+145
-27
index.tsx
web/app/components/workflow/header/index.tsx
+10
-2
run-and-history.tsx
web/app/components/workflow/header/run-and-history.tsx
+6
-2
hooks.ts
web/app/components/workflow/hooks.ts
+28
-1
index.tsx
web/app/components/workflow/index.tsx
+6
-0
index.tsx
web/app/components/workflow/operator/index.tsx
+0
-1
index.tsx
web/app/components/workflow/panel/index.tsx
+7
-2
inputs-panel.tsx
web/app/components/workflow/panel/inputs-panel.tsx
+6
-1
run-history.tsx
web/app/components/workflow/panel/run-history.tsx
+47
-18
types.ts
web/app/components/workflow/types.ts
+2
-0
workflow.ts
web/service/workflow.ts
+5
-0
workflow.ts
web/types/workflow.ts
+28
-0
No files found.
web/app/components/workflow/header/index.tsx
View file @
7655d7f6
...
...
@@ -5,7 +5,10 @@ import {
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useStore
}
from
'../store'
import
{
useIsChatMode
}
from
'../hooks'
import
{
useIsChatMode
,
useWorkflow
,
}
from
'../hooks'
import
RunAndHistory
from
'./run-and-history'
import
EditingTitle
from
'./editing-title'
import
RunningTitle
from
'./running-title'
...
...
@@ -21,11 +24,16 @@ const Header: FC = () => {
const
appSidebarExpand
=
useAppStore
(
s
=>
s
.
appSidebarExpand
)
const
isChatMode
=
useIsChatMode
()
const
runningStatus
=
useStore
(
s
=>
s
.
runningStatus
)
const
{
handleRunInit
}
=
useWorkflow
()
const
handleShowFeatures
=
useCallback
(()
=>
{
useStore
.
setState
({
showFeaturesPanel
:
true
})
},
[])
const
handleGoBackToEdit
=
useCallback
(()
=>
{
handleRunInit
(
true
)
},
[
handleRunInit
])
return
(
<
div
className=
'absolute top-0 left-0 flex items-center justify-between px-3 w-full h-14 z-10'
...
...
@@ -54,7 +62,7 @@ const Header: FC = () => {
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
border-[0.5px] border-gray-200 shadow-xs
`
}
onClick=
{
()
=>
useStore
.
setState
({
runningStatus
:
undefined
})
}
onClick=
{
handleGoBackToEdit
}
>
<
ArrowNarrowLeft
className=
'mr-1 w-4 h-4'
/>
{
t
(
'workflow.common.goBackToEdit'
)
}
...
...
web/app/components/workflow/header/run-and-history.tsx
View file @
7655d7f6
...
...
@@ -2,7 +2,10 @@ import type { FC } from 'react'
import
{
memo
}
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
{
useStore
}
from
'../store'
import
{
useIsChatMode
}
from
'../hooks'
import
{
useIsChatMode
,
useWorkflow
,
}
from
'../hooks'
import
{
WorkflowRunningStatus
}
from
'../types'
import
{
Play
}
from
'@/app/components/base/icons/src/vender/line/mediaAndDevices'
import
{
ClockPlay
}
from
'@/app/components/base/icons/src/vender/line/time'
...
...
@@ -51,11 +54,12 @@ RunMode.displayName = 'RunMode'
const
PreviewMode
=
memo
(()
=>
{
const
{
t
}
=
useTranslation
()
const
{
handleRunInit
}
=
useWorkflow
()
const
runningStatus
=
useStore
(
s
=>
s
.
runningStatus
)
const
isRunning
=
runningStatus
===
WorkflowRunningStatus
.
Running
const
handleClick
=
()
=>
{
useStore
.
setState
({
runningStatus
:
WorkflowRunningStatus
.
Succeeded
}
)
handleRunInit
(
)
}
return
(
...
...
web/app/components/workflow/hooks.ts
View file @
7655d7f6
...
...
@@ -79,11 +79,19 @@ export const useWorkflow = () => {
if
(
appId
)
{
const
features
=
featuresStore
!
.
getState
().
features
const
nodes
=
produce
(
getNodes
(),
(
draft
)
=>
{
draft
.
forEach
((
node
)
=>
{
Object
.
keys
(
node
.
data
).
forEach
((
key
)
=>
{
if
(
key
.
startsWith
(
'_'
))
delete
node
.
data
[
key
]
})
})
})
syncWorkflowDraft
({
url
:
`/apps/
${
appId
}
/workflows/draft`
,
params
:
{
graph
:
{
nodes
:
getNodes
()
,
nodes
,
edges
,
viewport
:
getViewport
(),
},
...
...
@@ -622,6 +630,17 @@ export const useWorkflow = () => {
setEdges
(
newEdges
)
},
[
store
])
const
handleRunInit
=
useCallback
((
shouldClear
?:
boolean
)
=>
{
useStore
.
setState
({
runningStatus
:
shouldClear
?
undefined
:
WorkflowRunningStatus
.
Waiting
})
const
{
setNodes
,
getNodes
}
=
store
.
getState
()
const
newNodes
=
produce
(
getNodes
(),
(
draft
)
=>
{
draft
.
forEach
((
node
)
=>
{
node
.
data
.
_runningStatus
=
shouldClear
?
undefined
:
NodeRunningStatus
.
Waiting
})
})
setNodes
(
newNodes
)
},
[
store
])
return
{
handleSyncWorkflowDraft
,
handleLayout
,
...
...
@@ -644,6 +663,8 @@ export const useWorkflow = () => {
handleEdgeLeave
,
handleEdgeDelete
,
handleEdgesChange
,
handleRunInit
,
}
}
...
...
@@ -674,6 +695,12 @@ export const useWorkflowRun = () => {
useStore
.
setState
({
runningStatus
:
WorkflowRunningStatus
.
Running
})
useStore
.
setState
({
taskId
:
task_id
})
useStore
.
setState
({
workflowRunId
:
workflow_run_id
})
const
newNodes
=
produce
(
getNodes
(),
(
draft
)
=>
{
draft
.
forEach
((
node
)
=>
{
node
.
data
.
_runningStatus
=
NodeRunningStatus
.
Waiting
})
})
setNodes
(
newNodes
)
},
onWorkflowFinished
:
({
data
})
=>
{
useStore
.
setState
({
runningStatus
:
data
.
status
as
WorkflowRunningStatus
})
...
...
web/app/components/workflow/index.tsx
View file @
7655d7f6
...
...
@@ -63,6 +63,7 @@ const Workflow: FC<WorkflowProps> = memo(({
viewport
,
})
=>
{
const
showFeaturesPanel
=
useStore
(
state
=>
state
.
showFeaturesPanel
)
const
runningStatus
=
useStore
(
s
=>
s
.
runningStatus
)
const
{
handleSyncWorkflowDraft
,
...
...
@@ -116,6 +117,11 @@ const Workflow: FC<WorkflowProps> = memo(({
deleteKeyCode=
{
null
}
nodeDragThreshold=
{
1
}
defaultViewport=
{
viewport
}
panOnDrag=
{
!
runningStatus
}
nodesDraggable=
{
!
runningStatus
}
nodesConnectable=
{
!
runningStatus
}
nodesFocusable=
{
!
runningStatus
}
edgesFocusable=
{
!
runningStatus
}
>
<
Background
gap=
{
[
14
,
14
]
}
...
...
web/app/components/workflow/operator/index.tsx
View file @
7655d7f6
...
...
@@ -18,7 +18,6 @@ const Operator = () => {
height
:
80
,
}
}
className=
'!static !m-0 !w-[128px] !h-[80px] !border-[0.5px] !border-black/[0.08] !rounded-lg !shadow-lg'
pannable
/>
<
div
className=
'flex items-center mt-1 p-0.5 rounded-lg border-[0.5px] border-gray-100 bg-white shadow-lg text-gray-500'
>
<
ZoomInOut
/>
...
...
web/app/components/workflow/panel/index.tsx
View file @
7655d7f6
...
...
@@ -30,12 +30,17 @@ const Panel: FC = () => {
return
{
showWorkflowInfoPanel
:
!
isChatMode
&&
!
selectedNode
&&
!
runningStatus
,
showNodePanel
:
!!
selectedNode
&&
!
runningStatus
,
showDebugAndPreviewPanel
:
isChatMode
&&
!
selectedNode
&&
!
runningStatus
,
showDebugAndPreviewPanel
:
isChatMode
&&
runningStatus
,
}
},
[
selectedNode
,
isChatMode
,
runningStatus
])
return
(
<
div
className=
'absolute top-14 right-0 bottom-2 flex z-10'
>
<
div
className=
{
`
absolute top-14 right-0 bottom-2 flex pr-2 z-10
${showRunHistory && '!pr-0'}
`
}
>
{
showInputsPanel
&&
(
<
InputsPanel
/>
...
...
web/app/components/workflow/panel/inputs-panel.tsx
View file @
7655d7f6
...
...
@@ -7,7 +7,10 @@ import { useNodes } from 'reactflow'
import
FormItem
from
'../nodes/_base/components/before-run-form/form-item'
import
{
BlockEnum
}
from
'../types'
import
{
useStore
}
from
'../store'
import
{
useWorkflowRun
}
from
'../hooks'
import
{
useWorkflow
,
useWorkflowRun
,
}
from
'../hooks'
import
type
{
StartNodeType
}
from
'../nodes/start/types'
import
Button
from
'@/app/components/base/button'
...
...
@@ -16,6 +19,7 @@ const InputsPanel = () => {
const
nodes
=
useNodes
<
StartNodeType
>
()
const
inputs
=
useStore
(
s
=>
s
.
inputs
)
const
run
=
useWorkflowRun
()
const
{
handleRunInit
}
=
useWorkflow
()
const
startNode
=
nodes
.
find
(
node
=>
node
.
data
.
type
===
BlockEnum
.
Start
)
const
variables
=
startNode
?.
data
.
variables
||
[]
...
...
@@ -32,6 +36,7 @@ const InputsPanel = () => {
const
handleRun
=
()
=>
{
handleCancel
()
handleRunInit
()
run
({
inputs
})
}
...
...
web/app/components/workflow/panel/run-history.tsx
View file @
7655d7f6
import
{
memo
}
from
'react'
import
dayjs
from
'dayjs'
import
{
useTranslation
}
from
'react-i18next'
import
useSWR
from
'swr'
import
{
WorkflowRunningStatus
}
from
'../types'
import
{
XClose
}
from
'@/app/components/base/icons/src/vender/line/general'
import
{
AlertCircle
}
from
'@/app/components/base/icons/src/vender/line/alertsAndFeedback'
import
{
CheckCircle
}
from
'@/app/components/base/icons/src/vender/solid/general'
import
{
useStore
}
from
'@/app/components/workflow/store'
import
{
useStore
as
useAppStore
}
from
'@/app/components/app/store'
import
{
fetchWorkflowRunHistory
}
from
'@/service/workflow'
import
Loading
from
'@/app/components/base/loading'
const
RunHistory
=
()
=>
{
const
{
t
}
=
useTranslation
()
const
appDetail
=
useAppStore
(
state
=>
state
.
appDetail
)
const
{
data
,
isLoading
}
=
useSWR
(
appDetail
?
`/apps/
${
appDetail
.
id
}
/workflow-runs`
:
null
,
fetchWorkflowRunHistory
)
if
(
!
appDetail
)
return
null
return
(
<
div
className=
'ml-2 w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'
>
<
div
className=
'flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'
>
<
div
className=
'
flex flex-col
ml-2 w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'
>
<
div
className=
'
shrink-0
flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'
>
{
t
(
'workflow.common.runHistory'
)
}
<
div
className=
'flex items-center justify-center w-6 h-6 cursor-pointer'
...
...
@@ -20,23 +30,42 @@ const RunHistory = () => {
<
XClose
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
</
div
>
<
div
className=
'p-2'
>
<
div
className=
'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
onClick=
{
()
=>
useStore
.
setState
({
runTaskId
:
'1'
})
}
>
{
appDetail
?.
mode
===
'advanced-chat'
&&
(
<
AlertCircle
className=
'mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]'
/>
)
}
<
div
>
<
div
className=
'flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'
>
Test Run#6
</
div
>
<
div
className=
'flex items-center text-xs text-gray-500 leading-[18px]'
>
Evan · 30 min ago
</
div
>
{
isLoading
&&
(
<
div
className=
'grow flex items-center justify-center h-full'
>
<
Loading
/>
</
div
>
</
div
>
)
}
<
div
className=
'grow p-2 overflow-y-auto'
>
{
data
?.
data
.
map
(
item
=>
(
<
div
key=
{
item
.
id
}
className=
'flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
onClick=
{
()
=>
useStore
.
setState
({
workflowRunId
:
item
.
id
})
}
>
{
appDetail
?.
mode
===
'workflow'
&&
item
.
status
===
WorkflowRunningStatus
.
Failed
&&
(
<
AlertCircle
className=
'mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]'
/>
)
}
{
appDetail
?.
mode
===
'workflow'
&&
item
.
status
===
WorkflowRunningStatus
.
Succeeded
&&
(
<
CheckCircle
className=
'mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#12B76A]'
/>
)
}
<
div
>
<
div
className=
'flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'
>
Test Run#
{
item
.
sequence_number
}
</
div
>
<
div
className=
'flex items-center text-xs text-gray-500 leading-[18px]'
>
{
item
.
created_by_account
.
name
}
·
{
dayjs
((
item
.
finished_at
||
item
.
created_at
)
*
1000
).
fromNow
()
}
</
div
>
</
div
>
</
div
>
))
}
</
div
>
</
div
>
)
...
...
web/app/components/workflow/types.ts
View file @
7655d7f6
...
...
@@ -150,6 +150,7 @@ export enum Mode {
}
export
enum
WorkflowRunningStatus
{
Waiting
=
'waiting'
,
Running
=
'running'
,
Succeeded
=
'succeeded'
,
Failed
=
'failed'
,
...
...
@@ -157,6 +158,7 @@ export enum WorkflowRunningStatus {
}
export
enum
NodeRunningStatus
{
Waiting
=
'waiting'
,
Running
=
'running'
,
Succeeded
=
'succeeded'
,
Failed
=
'failed'
,
...
...
web/service/workflow.ts
View file @
7655d7f6
...
...
@@ -3,6 +3,7 @@ import { get, post } from './base'
import
type
{
CommonResponse
}
from
'@/models/common'
import
type
{
FetchWorkflowDraftResponse
,
WorkflowRunHistoryResponse
,
}
from
'@/types/workflow'
export
const
fetchWorkflowDraft
:
Fetcher
<
FetchWorkflowDraftResponse
,
string
>
=
(
url
)
=>
{
...
...
@@ -16,3 +17,7 @@ export const syncWorkflowDraft = ({ url, params }: { url: string; params: Pick<F
export
const
fetchNodesDefaultConfigs
:
Fetcher
<
any
,
string
>
=
(
url
)
=>
{
return
get
<
any
>
(
url
)
}
export
const
fetchWorkflowRunHistory
:
Fetcher
<
WorkflowRunHistoryResponse
,
string
>
=
(
url
)
=>
{
return
get
<
WorkflowRunHistoryResponse
>
(
url
)
}
web/types/workflow.ts
View file @
7655d7f6
...
...
@@ -131,3 +131,31 @@ export type TextReplaceResponse = {
text
:
string
}
}
export
type
WorkflowRunHistory
=
{
id
:
string
sequence_number
:
number
version
:
string
graph
:
{
nodes
:
Node
[]
edges
:
Edge
[]
viewport
?:
Viewport
}
inputs
:
Record
<
string
,
string
>
status
:
string
outputs
:
Record
<
string
,
any
>
error
?:
string
elapsed_time
:
number
total_tokens
:
number
total_steps
:
number
created_at
:
number
finished_at
:
number
created_by_account
:
{
id
:
string
name
:
string
email
:
string
}
}
export
type
WorkflowRunHistoryResponse
=
{
data
:
WorkflowRunHistory
[]
}
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