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
9693d014
Commit
9693d014
authored
Mar 07, 2024
by
Joel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: add llm debug
parent
16abcf08
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
355 additions
and
32 deletions
+355
-32
constants.ts
web/app/components/workflow/constants.ts
+20
-0
form-item.tsx
...flow/nodes/_base/components/before-run-form/form-item.tsx
+137
-0
form.tsx
.../workflow/nodes/_base/components/before-run-form/form.tsx
+53
-3
index.tsx
...workflow/nodes/_base/components/before-run-form/index.tsx
+39
-24
use-one-step-run.ts
...components/workflow/nodes/_base/hooks/use-one-step-run.ts
+21
-3
mock.ts
web/app/components/workflow/nodes/llm/mock.ts
+8
-0
panel.tsx
web/app/components/workflow/nodes/llm/panel.tsx
+53
-2
use-config.ts
web/app/components/workflow/nodes/llm/use-config.ts
+15
-0
types.ts
web/app/components/workflow/types.ts
+1
-0
workflow.ts
web/i18n/en-US/workflow.ts
+4
-0
workflow.ts
web/i18n/zh-Hans/workflow.ts
+4
-0
No files found.
web/app/components/workflow/constants.ts
View file @
9693d014
...
...
@@ -117,3 +117,23 @@ export const NODE_WIDTH = 220
export
const
X_OFFSET
=
64
export
const
Y_OFFSET
=
39
export
const
TREE_DEEPTH
=
20
export
const
RETRIEVAL_OUTPUT_STRUCT
=
`{
"content": "",
"title": "",
"url": "",
"icon": "",
"metadata": {
"dataset_id": "",
"dataset_name": "",
"document_id": [],
"document_name": "",
"document_data_source_type": "",
"segment_id": "",
"segment_position": "",
"segment_word_count": "",
"segment_hit_count": "",
"segment_index_node_hash": "",
"score": ""
}
}`
web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx
0 → 100644
View file @
9693d014
'use client'
import
type
{
FC
}
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
produce
from
'immer'
import
type
{
InputVar
}
from
'../../../../types'
import
{
InputVarType
}
from
'../../../../types'
import
CodeEditor
from
'../editor/code-editor'
import
{
CodeLanguage
}
from
'../../../code/types'
import
Select
from
'@/app/components/base/select'
import
TextGenerationImageUploader
from
'@/app/components/base/image-uploader/text-generation-image-uploader'
import
{
Resolution
,
TransferMethod
}
from
'@/types/app'
import
{
Trash03
}
from
'@/app/components/base/icons/src/vender/line/general'
type
Props
=
{
payload
:
InputVar
value
:
any
onChange
:
(
value
:
any
)
=>
void
}
const
FormItem
:
FC
<
Props
>
=
({
payload
,
value
,
onChange
,
})
=>
{
const
{
type
}
=
payload
const
handleContextItemChange
=
useCallback
((
index
:
number
)
=>
{
return
(
newValue
:
any
)
=>
{
const
newValues
=
produce
(
value
,
(
draft
:
any
)
=>
{
draft
[
index
]
=
newValue
})
onChange
(
newValues
)
}
},
[
value
,
onChange
])
const
handleContextItemRemove
=
useCallback
((
index
:
number
)
=>
{
return
()
=>
{
const
newValues
=
produce
(
value
,
(
draft
:
any
)
=>
{
draft
.
splice
(
index
,
1
)
})
onChange
(
newValues
)
}
},
[
value
,
onChange
])
return
(
<
div
className=
'flex justify-between items-start'
>
{
type
!==
InputVarType
.
contexts
&&
<
div
className=
'shrink-0 w-[96px] pr-1 h-8 leading-8 text-[13px] font-medium text-gray-700 capitalize truncate'
>
{
payload
.
label
}
</
div
>
}
<
div
className=
'w-0 grow'
>
{
type
===
InputVarType
.
textInput
&&
(
<
input
className=
"w-full px-3 text-sm leading-8 text-gray-900 border-0 rounded-lg grow h-8 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
type=
"text"
value=
{
value
||
''
}
onChange=
{
e
=>
onChange
(
e
.
target
.
value
)
}
/>
)
}
{
type
===
InputVarType
.
number
&&
(
<
input
className=
"w-full px-3 text-sm leading-8 text-gray-900 border-0 rounded-lg grow h-8 bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
type=
"number"
value=
{
value
||
''
}
onChange=
{
e
=>
onChange
(
e
.
target
.
value
)
}
/>
)
}
{
type
===
InputVarType
.
paragraph
&&
(
<
textarea
className=
"w-full px-3 text-sm leading-8 text-gray-900 border-0 rounded-lg grow h-[120px] bg-gray-50 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200"
value=
{
value
||
''
}
onChange=
{
e
=>
onChange
(
e
.
target
.
value
)
}
/>
)
}
{
type
===
InputVarType
.
select
&&
(
<
Select
className=
"w-full"
defaultValue=
{
value
||
''
}
items=
{
payload
.
options
?.
map
(
option
=>
({
name
:
option
,
value
:
option
}))
||
[]
}
onSelect=
{
i
=>
onChange
(
i
.
value
)
}
allowSearch=
{
false
}
/>
)
}
{
type
===
InputVarType
.
files
&&
(
<
TextGenerationImageUploader
settings=
{
{
enabled
:
true
,
number_limits
:
3
,
detail
:
Resolution
.
high
,
transfer_methods
:
[
TransferMethod
.
local_file
,
TransferMethod
.
remote_url
],
}
}
onFilesChange=
{
files
=>
onChange
(
files
.
filter
(
file
=>
file
.
progress
!==
-
1
).
map
(
fileItem
=>
({
type
:
'image'
,
transfer_method
:
fileItem
.
type
,
url
:
fileItem
.
url
,
upload_file_id
:
fileItem
.
fileId
,
})))
}
/>
)
}
{
type
===
InputVarType
.
contexts
&&
(
<
div
className=
'space-y-2'
>
{
(
value
||
[]).
map
((
item
:
any
,
index
:
number
)
=>
(
<
CodeEditor
key=
{
index
}
value=
{
item
}
title=
{
<
span
>
JSON
</
span
>
}
headerRight=
{
(
value
as
any
).
length
>
1
?
(<
Trash03
onClick=
{
handleContextItemRemove
(
index
)
}
className=
'mr-1 w-3.5 h-3.5 text-gray-500 cursor-pointer'
/>)
:
undefined
}
language=
{
CodeLanguage
.
json
}
onChange=
{
handleContextItemChange
(
index
)
}
/>
))
}
</
div
>
)
}
</
div
>
</
div
>
)
}
export
default
React
.
memo
(
FormItem
)
web/app/components/workflow/nodes/_base/components/before-run-form/form.tsx
View file @
9693d014
'use client'
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
React
,
{
useCallback
}
from
'react'
import
produce
from
'immer'
import
cn
from
'classnames'
import
type
{
InputVar
}
from
'../../../../types'
import
FormItem
from
'./form-item'
import
{
InputVarType
}
from
'@/app/components/workflow/types'
import
AddButton
from
'@/app/components/base/button/add-button'
import
{
RETRIEVAL_OUTPUT_STRUCT
}
from
'@/app/components/workflow/constants'
export
type
Props
=
{
className
?:
string
label
?:
string
inputs
:
InputVar
[]
values
:
Record
<
string
,
string
>
onChange
:
(
newValues
:
Record
<
string
,
any
>
)
=>
void
}
const
Form
:
FC
<
Props
>
=
()
=>
{
const
Form
:
FC
<
Props
>
=
({
className
,
label
,
inputs
,
values
,
onChange
,
})
=>
{
const
handleChange
=
useCallback
((
key
:
string
)
=>
{
return
(
value
:
any
)
=>
{
const
newValues
=
produce
(
values
,
(
draft
)
=>
{
draft
[
key
]
=
value
})
onChange
(
newValues
)
}
},
[
values
,
onChange
])
const
handleAddContext
=
useCallback
(()
=>
{
const
newValues
=
produce
(
values
,
(
draft
:
any
)
=>
{
const
key
=
inputs
[
0
].
variable
draft
[
key
].
push
(
RETRIEVAL_OUTPUT_STRUCT
)
})
onChange
(
newValues
)
},
[
values
,
onChange
])
return
(
<
div
>
<
div
className=
{
cn
(
className
,
'space-y-2'
)
}
>
{
label
&&
(
<
div
className=
'mb-1 flex items-center justify-between'
>
<
div
className=
'flex items-center h-6 text-xs font-medium text-gray-500 uppercase'
>
{
label
}
</
div
>
{
inputs
[
0
]?.
type
===
InputVarType
.
contexts
&&
(
<
AddButton
onClick=
{
handleAddContext
}
/>
)
}
</
div
>
)
}
{
inputs
.
map
((
input
,
index
)
=>
{
return
(
<
FormItem
key=
{
index
}
payload=
{
input
}
value=
{
values
[
input
.
variable
]
}
onChange=
{
handleChange
(
input
.
variable
)
}
/>
)
})
}
</
div
>
)
}
...
...
web/app/components/workflow/nodes/_base/components/before-run-form/index.tsx
View file @
9693d014
...
...
@@ -2,21 +2,24 @@
import
type
{
FC
}
from
'react'
import
React
from
'react'
import
{
useTranslation
}
from
'react-i18next'
import
type
{
Props
}
from
'./form'
import
cn
from
'classnames'
import
type
{
Props
as
FormProps
}
from
'./form'
import
Form
from
'./form'
import
Button
from
'@/app/components/base/button'
import
{
StopCircle
}
from
'@/app/components/base/icons/src/vender/solid/mediaAndDevices'
import
{
Loading02
,
XClose
}
from
'@/app/components/base/icons/src/vender/line/general'
import
Split
from
'@/app/components/workflow/nodes/_base/components/split'
const
i18nPrefix
=
'workflow.singleRun'
type
BeforeRunFormProps
=
Props
&
{
type
BeforeRunFormProps
=
{
nodeName
:
string
onHide
:
()
=>
void
onRun
:
()
=>
void
onStop
:
()
=>
void
runningStatus
:
string
// todo: wait for enum
result
?:
JSX
.
Element
forms
:
FormProps
[]
}
const
BeforeRunForm
:
FC
<
BeforeRunFormProps
>
=
({
nodeName
,
...
...
@@ -25,7 +28,7 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
onStop
,
runningStatus
,
result
,
...
formProps
forms
,
})
=>
{
const
{
t
}
=
useTranslation
()
...
...
@@ -35,8 +38,8 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
<
div
className=
'absolute inset-0 z-10 rounded-2xl pt-10'
style=
{
{
backgroundColor
:
'rgba(16, 24, 40, 0.20)'
,
}
}
>
<
div
className=
'h-full rounded-2xl bg-white'
>
<
div
className=
'flex justify-between items-center h-8 pl-4 pr-3 pt-3'
>
<
div
className=
'h-full rounded-2xl bg-white
flex flex-col
'
>
<
div
className=
'
shrink-0
flex justify-between items-center h-8 pl-4 pr-3 pt-3'
>
<
div
className=
'text-base font-semibold text-gray-900 truncate'
>
{
t
(
`${i18nPrefix}.testRun`
)
}
{
nodeName
}
</
div
>
...
...
@@ -45,30 +48,42 @@ const BeforeRunForm: FC<BeforeRunFormProps> = ({
</
div
>
</
div
>
<
div
className=
'px-4'
>
<
Form
{
...
formProps
}
/>
</
div
>
<
div
className=
'h-0 grow overflow-y-auto pb-4'
>
<
div
className=
'mt-3 px-4 space-y-4'
>
{
forms
.
map
((
form
,
index
)
=>
(
<
div
key=
{
index
}
>
<
Form
key=
{
index
}
className=
{
cn
(
index
<
forms
.
length
-
1
&&
'mb-4'
)
}
{
...
form
}
/>
{
index
<
forms
.
length
-
1
&&
<
Split
/>
}
</
div
>
))
}
</
div
>
<
div
className=
'mt-4 flex justify-between space-x-2 px-4'
>
{
isRunning
&&
(
<
div
className=
'p-2 rounded-lg border border-gray-200 bg-white shadow-xs cursor-pointer'
onClick=
{
onStop
}
>
<
StopCircle
className=
'w-4 h-4 text-gray-500'
/>
<
div
className=
'mt-4 flex justify-between space-x-2 px-4'
>
{
isRunning
&&
(
<
div
className=
'p-2 rounded-lg border border-gray-200 bg-white shadow-xs cursor-pointer'
onClick=
{
onStop
}
>
<
StopCircle
className=
'w-4 h-4 text-gray-500'
/>
</
div
>
)
}
<
Button
disabled=
{
isRunning
}
type=
'primary'
className=
'w-0 grow !h-8 flex items-center space-x-2'
onClick=
{
onRun
}
>
{
isRunning
&&
<
Loading02
className=
'animate-spin w-4 h-4 text-white'
/>
}
<
div
>
{
t
(
`${i18nPrefix}.${isRunning ? 'running' : 'startRun'}`
)
}
</
div
>
</
Button
>
</
div
>
{
isFinished
&&
(
<
div
className=
'px-4'
>
{
result
}
</
div
>
)
}
<
Button
disabled=
{
isRunning
}
type=
'primary'
className=
'w-0 grow !h-8 flex items-center space-x-2'
onClick=
{
onRun
}
>
{
isRunning
&&
<
Loading02
className=
'animate-spin w-4 h-4 text-white'
/>
}
<
div
>
{
t
(
`${i18nPrefix}.${isRunning ? 'running' : 'startRun'}`
)
}
</
div
>
</
Button
>
</
div
>
{
isFinished
&&
(
<
div
className=
'px-4'
>
{
result
}
</
div
>
)
}
</
div
>
</
div
>
)
...
...
web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts
View file @
9693d014
...
...
@@ -2,6 +2,7 @@ import { useState } from 'react'
import
{
useWorkflow
}
from
'@/app/components/workflow/hooks'
import
type
{
CommonNodeType
,
InputVar
,
Variable
}
from
'@/app/components/workflow/types'
import
{
InputVarType
}
from
'@/app/components/workflow/types'
import
{
RETRIEVAL_OUTPUT_STRUCT
}
from
'@/app/components/workflow/constants'
type
Params
<
T
>
=
{
id
:
string
...
...
@@ -23,17 +24,28 @@ const useOneStepRun = <T>({ id, data }: Params<T>) => {
const
[
runningStatus
,
setRunningStatus
]
=
useState
(
'un started'
)
// TODO: test
const
[
inputVarValues
,
setInputVarValues
]
=
useState
<
Record
<
string
,
any
>>
({
name
:
'Joel'
,
age
:
'18'
,
})
const
[
visionFiles
,
setVisionFiles
]
=
useState
<
any
[]
>
([])
const
[
contexts
,
setContexts
]
=
useState
<
string
[]
>
([
RETRIEVAL_OUTPUT_STRUCT
])
const
toVarInputs
=
(
variables
:
Variable
[]):
InputVar
[]
=>
{
if
(
!
variables
)
return
[]
const
varInputs
=
variables
.
map
((
item
)
=>
{
const
varInputs
=
variables
.
map
((
item
,
i
)
=>
{
const
allVarTypes
=
[
InputVarType
.
textInput
,
InputVarType
.
paragraph
,
InputVarType
.
number
,
InputVarType
.
select
,
InputVarType
.
files
]
return
{
label
:
item
.
variable
,
variable
:
item
.
variable
,
type
:
InputVarType
.
textInput
,
// TODO: dynamic get var type
type
:
allVarTypes
[
i
%
allVarTypes
.
length
]
,
// TODO: dynamic get var type
required
:
true
,
// TODO
options
:
[],
// TODO
options
:
[
'a'
,
'b'
,
'c'
],
// TODO
}
})
...
...
@@ -46,6 +58,12 @@ const useOneStepRun = <T>({ id, data }: Params<T>) => {
toVarInputs
,
runningStatus
,
setRunningStatus
,
inputVarValues
,
setInputVarValues
,
visionFiles
,
setVisionFiles
,
contexts
,
setContexts
,
}
}
...
...
web/app/components/workflow/nodes/llm/mock.ts
View file @
9693d014
...
...
@@ -23,6 +23,14 @@ export const mockData: LLMNodeType = {
variable
:
'age'
,
value_selector
:
[
'bbb'
,
'b'
,
'c'
],
},
{
variable
:
'a1'
,
value_selector
:
[
'aaa'
,
'name'
],
},
{
variable
:
'a2'
,
value_selector
:
[
'aaa'
,
'name'
],
},
],
prompt
:
[
{
...
...
web/app/components/workflow/nodes/llm/panel.tsx
View file @
9693d014
...
...
@@ -14,7 +14,7 @@ import Split from '@/app/components/workflow/nodes/_base/components/split'
import
ModelParameterModal
from
'@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import
OutputVars
,
{
VarItem
}
from
'@/app/components/workflow/nodes/_base/components/output-vars'
import
{
Resolution
}
from
'@/types/app'
import
type
{
NodePanelProps
}
from
'@/app/components/workflow/types'
import
{
InputVarType
,
type
NodePanelProps
}
from
'@/app/components/workflow/types'
import
BeforeRunForm
from
'@/app/components/workflow/nodes/_base/components/before-run-form'
const
i18nPrefix
=
'workflow.nodes.llm'
...
...
@@ -41,6 +41,12 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
handleVisionResolutionChange
,
isShowSingleRun
,
hideSingleRun
,
inputVarValues
,
setInputVarValues
,
visionFiles
,
setVisionFiles
,
contexts
,
setContexts
,
runningStatus
,
handleRun
,
handleStop
,
...
...
@@ -50,6 +56,51 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
const
isChatApp
=
true
// TODO: get from app context
const
model
=
inputs
.
model
const
singleRunForms
=
(()
=>
{
const
forms
=
[
{
label
:
t
(
`
${
i18nPrefix
}
.singleRun.variable`
)
!
,
inputs
:
varInputs
,
values
:
inputVarValues
,
onChange
:
setInputVarValues
,
},
]
if
(
inputs
.
context
?.
variable_selector
&&
inputs
.
context
?.
variable_selector
.
length
>
0
)
{
forms
.
push
(
{
label
:
t
(
`
${
i18nPrefix
}
.context`
)
!
,
inputs
:
[{
label
:
''
,
variable
:
'contexts'
,
type
:
InputVarType
.
contexts
,
required
:
false
,
}],
values
:
{
contexts
},
onChange
:
keyValue
=>
setContexts
((
keyValue
as
any
).
contexts
),
},
)
}
if
(
isShowVisionConfig
)
{
forms
.
push
(
{
label
:
t
(
`
${
i18nPrefix
}
.vision`
)
!
,
inputs
:
[{
label
:
t
(
`
${
i18nPrefix
}
.files`
)
!
,
variable
:
'visionFiles'
,
type
:
InputVarType
.
files
,
required
:
false
,
}],
values
:
{
visionFiles
},
onChange
:
keyValue
=>
setVisionFiles
((
keyValue
as
any
).
visionFiles
),
},
)
}
return
forms
})()
return
(
<
div
className=
'mt-2'
>
<
div
className=
'px-4 pb-4 space-y-4'
>
...
...
@@ -159,7 +210,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
<
BeforeRunForm
nodeName=
{
inputs
.
title
}
onHide=
{
hideSingleRun
}
inputs=
{
varInput
s
}
forms=
{
singleRunForm
s
}
runningStatus=
{
runningStatus
}
onRun=
{
handleRun
}
onStop=
{
handleStop
}
...
...
web/app/components/workflow/nodes/llm/use-config.ts
View file @
9693d014
...
...
@@ -95,10 +95,19 @@ const useConfig = (id: string, payload: LLMNodeType) => {
toVarInputs
,
runningStatus
,
setRunningStatus
,
inputVarValues
,
setInputVarValues
,
visionFiles
,
setVisionFiles
,
contexts
,
setContexts
,
}
=
useOneStepRun
<
LLMNodeType
>
({
id
,
data
:
inputs
,
})
console
.
log
(
contexts
)
const
varInputs
=
toVarInputs
(
inputs
.
variables
)
const
handleRun
=
()
=>
{
setRunningStatus
(
'running'
)
...
...
@@ -123,6 +132,12 @@ const useConfig = (id: string, payload: LLMNodeType) => {
handleVisionResolutionChange
,
isShowSingleRun
,
hideSingleRun
,
inputVarValues
,
setInputVarValues
,
visionFiles
,
setVisionFiles
,
contexts
,
setContexts
,
varInputs
,
runningStatus
,
handleRun
,
...
...
web/app/components/workflow/types.ts
View file @
9693d014
...
...
@@ -70,6 +70,7 @@ export enum InputVarType {
number
=
'number'
,
url
=
'url'
,
files
=
'files'
,
contexts
=
'contexts'
,
// knowledge retrieval
}
export
type
InputVar
=
{
...
...
web/i18n/en-US/workflow.ts
View file @
9693d014
...
...
@@ -124,6 +124,7 @@ const translation = {
roleDescription
:
'TODO: Role Description'
,
addMessage
:
'Add Message'
,
vision
:
'vision'
,
files
:
'Files'
,
resolution
:
{
name
:
'Resolution'
,
high
:
'High'
,
...
...
@@ -133,6 +134,9 @@ const translation = {
output
:
'Generate content'
,
usage
:
'Model Usage Information'
,
},
singleRun
:
{
variable
:
'Variable'
,
},
},
knowledgeRetrieval
:
{
queryVariable
:
'Query Variable'
,
...
...
web/i18n/zh-Hans/workflow.ts
View file @
9693d014
...
...
@@ -124,6 +124,7 @@ const translation = {
addMessage
:
'添加消息'
,
roleDescription
:
'TODO: Role Description'
,
vision
:
'视觉'
,
files
:
'文件'
,
resolution
:
{
name
:
'分辨率'
,
high
:
'高'
,
...
...
@@ -133,6 +134,9 @@ const translation = {
output
:
'生成内容'
,
usage
:
'模型用量信息'
,
},
singleRun
:
{
variable
:
'变量'
,
},
},
knowledgeRetrieval
:
{
queryVariable
:
'查询变量'
,
...
...
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