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
1c450e27
Unverified
Commit
1c450e27
authored
Mar 11, 2024
by
Yeuoly
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: support empty code output children
parent
bbc76cb8
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
167 additions
and
96 deletions
+167
-96
code_node.py
api/core/workflow/nodes/code/code_node.py
+51
-2
entities.py
api/core/workflow/nodes/code/entities.py
+2
-2
test_code.py
api/tests/integration_tests/workflow/nodes/test_code.py
+114
-92
No files found.
api/core/workflow/nodes/code/code_node.py
View file @
1c450e27
...
...
@@ -153,11 +153,13 @@ class CodeNode(BaseNode):
raise
ValueError
(
f
'{variable} in input form is out of range.'
)
if
isinstance
(
value
,
float
):
value
=
round
(
value
,
MAX_PRECISION
)
# raise error if precision is too high
if
len
(
str
(
value
)
.
split
(
'.'
)[
1
])
>
MAX_PRECISION
:
raise
ValueError
(
f
'{variable} in output form has too high precision.'
)
return
value
def
_transform_result
(
self
,
result
:
dict
,
output_schema
:
dict
[
str
,
CodeNodeData
.
Output
],
def
_transform_result
(
self
,
result
:
dict
,
output_schema
:
Optional
[
dict
[
str
,
CodeNodeData
.
Output
]
],
prefix
:
str
=
''
,
depth
:
int
=
1
)
->
dict
:
"""
...
...
@@ -170,6 +172,47 @@ class CodeNode(BaseNode):
raise
ValueError
(
"Depth limit reached, object too deep."
)
transformed_result
=
{}
if
output_schema
is
None
:
# validate output thought instance type
for
output_name
,
output_value
in
result
.
items
():
if
isinstance
(
output_value
,
dict
):
self
.
_transform_result
(
result
=
output_value
,
output_schema
=
None
,
prefix
=
f
'{prefix}.{output_name}'
if
prefix
else
output_name
,
depth
=
depth
+
1
)
elif
isinstance
(
output_value
,
(
int
,
float
)):
self
.
_check_number
(
value
=
output_value
,
variable
=
f
'{prefix}.{output_name}'
if
prefix
else
output_name
)
elif
isinstance
(
output_value
,
str
):
self
.
_check_string
(
value
=
output_value
,
variable
=
f
'{prefix}.{output_name}'
if
prefix
else
output_name
)
elif
isinstance
(
output_value
,
list
):
if
all
(
isinstance
(
value
,
(
int
,
float
))
for
value
in
output_value
):
for
value
in
output_value
:
self
.
_check_number
(
value
=
value
,
variable
=
f
'{prefix}.{output_name}'
if
prefix
else
output_name
)
elif
all
(
isinstance
(
value
,
str
)
for
value
in
output_value
):
for
value
in
output_value
:
self
.
_check_string
(
value
=
value
,
variable
=
f
'{prefix}.{output_name}'
if
prefix
else
output_name
)
else
:
raise
ValueError
(
f
'Output {prefix}.{output_name} is not a valid array. make sure all elements are of the same type.'
)
else
:
raise
ValueError
(
f
'Output {prefix}.{output_name} is not a valid type.'
)
return
result
parameters_validated
=
{}
for
output_name
,
output_config
in
output_schema
.
items
():
if
output_config
.
type
==
'object'
:
# check if output is object
...
...
@@ -236,6 +279,12 @@ class CodeNode(BaseNode):
]
else
:
raise
ValueError
(
f
'Output type {output_config.type} is not supported.'
)
parameters_validated
[
output_name
]
=
True
# check if all output parameters are validated
if
len
(
parameters_validated
)
!=
len
(
result
):
raise
ValueError
(
'Not all output parameters are validated.'
)
return
transformed_result
...
...
api/core/workflow/nodes/code/entities.py
View file @
1c450e27
from
typing
import
Literal
,
Union
from
typing
import
Literal
,
Optional
from
pydantic
import
BaseModel
...
...
@@ -12,7 +12,7 @@ class CodeNodeData(BaseNodeData):
"""
class
Output
(
BaseModel
):
type
:
Literal
[
'string'
,
'number'
,
'object'
,
'array[string]'
,
'array[number]'
]
children
:
Union
[
None
,
dict
[
str
,
'Output'
]]
children
:
Optional
[
dict
[
str
,
'Output'
]]
variables
:
list
[
VariableSelector
]
answer
:
str
...
...
api/tests/integration_tests/workflow/nodes/test_code.py
View file @
1c450e27
import
pytest
from
core.app.entities.app_invoke_entities
import
InvokeFrom
from
core.workflow.entities.variable_pool
import
VariablePool
from
core.workflow.nodes.code.code_node
import
CodeNode
from
models.workflow
import
WorkflowNodeExecutionStatus
,
WorkflowRunStatus
from
models.workflow
import
WorkflowNodeExecutionStatus
from
tests.integration_tests.workflow.nodes.__mock.code_executor
import
setup_code_executor_mock
@
pytest
.
mark
.
parametrize
(
'setup_code_executor_mock'
,
[[
'none'
]],
indirect
=
True
)
...
...
@@ -15,30 +16,37 @@ def test_execute_code(setup_code_executor_mock):
'''
# trim first 4 spaces at the beginning of each line
code
=
'
\n
'
.
join
([
line
[
4
:]
for
line
in
code
.
split
(
'
\n
'
)])
node
=
CodeNode
(
config
=
{
'id'
:
'1'
,
'data'
:
{
'outputs'
:
{
'result'
:
{
'type'
:
'number'
,
node
=
CodeNode
(
tenant_id
=
'1'
,
app_id
=
'1'
,
workflow_id
=
'1'
,
user_id
=
'1'
,
user_from
=
InvokeFrom
.
WEB_APP
,
config
=
{
'id'
:
'1'
,
'data'
:
{
'outputs'
:
{
'result'
:
{
'type'
:
'number'
,
},
},
}
,
'title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
]
,
},
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
]
,
'answer'
:
'12
3'
,
'code_language'
:
'python3'
,
'code'
:
code
'title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
]
,
}
,
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
],
'answer'
:
'123'
,
'code_language'
:
'python
3'
,
'code'
:
code
}
}
}
)
)
# construct variable pool
pool
=
VariablePool
(
system_variables
=
{},
user_inputs
=
{})
...
...
@@ -61,30 +69,37 @@ def test_execute_code_output_validator(setup_code_executor_mock):
'''
# trim first 4 spaces at the beginning of each line
code
=
'
\n
'
.
join
([
line
[
4
:]
for
line
in
code
.
split
(
'
\n
'
)])
node
=
CodeNode
(
config
=
{
'id'
:
'1'
,
'data'
:
{
"outputs"
:
{
"result"
:
{
"type"
:
"string"
,
},
}
,
'
title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
]
,
node
=
CodeNode
(
tenant_id
=
'1'
,
app_id
=
'1'
,
workflow_id
=
'1'
,
user_id
=
'1'
,
user_from
=
InvokeFrom
.
WEB_APP
,
config
=
{
'id'
:
'1'
,
'
data'
:
{
"outputs"
:
{
"result"
:
{
"type"
:
"string"
,
}
,
},
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
],
'answer'
:
'123'
,
'code_language'
:
'python3'
,
'code'
:
code
'title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
],
},
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
],
'answer'
:
'123'
,
'code_language'
:
'python3'
,
'code'
:
code
}
}
}
)
)
# construct variable pool
pool
=
VariablePool
(
system_variables
=
{},
user_inputs
=
{})
...
...
@@ -108,60 +123,67 @@ def test_execute_code_output_validator_depth():
'''
# trim first 4 spaces at the beginning of each line
code
=
'
\n
'
.
join
([
line
[
4
:]
for
line
in
code
.
split
(
'
\n
'
)])
node
=
CodeNode
(
config
=
{
'id'
:
'1'
,
'data'
:
{
"outputs"
:
{
"string_validator"
:
{
"type"
:
"string"
,
},
"number_validator"
:
{
"type"
:
"number"
,
},
"number_array_validator"
:
{
"type"
:
"array[number]"
,
},
"string_array_validator"
:
{
"type"
:
"array[string]"
,
},
"object_validator"
:
{
"type"
:
"object"
,
"children"
:
{
"result"
:
{
"type"
:
"number"
,
},
"depth"
:
{
"type"
:
"object"
,
"children"
:
{
"depth"
:
{
"type"
:
"object"
,
"children"
:
{
"depth"
:
{
"type"
:
"number"
,
node
=
CodeNode
(
tenant_id
=
'1'
,
app_id
=
'1'
,
workflow_id
=
'1'
,
user_id
=
'1'
,
user_from
=
InvokeFrom
.
WEB_APP
,
config
=
{
'id'
:
'1'
,
'data'
:
{
"outputs"
:
{
"string_validator"
:
{
"type"
:
"string"
,
},
"number_validator"
:
{
"type"
:
"number"
,
},
"number_array_validator"
:
{
"type"
:
"array[number]"
,
},
"string_array_validator"
:
{
"type"
:
"array[string]"
,
},
"object_validator"
:
{
"type"
:
"object"
,
"children"
:
{
"result"
:
{
"type"
:
"number"
,
},
"depth"
:
{
"type"
:
"object"
,
"children"
:
{
"depth"
:
{
"type"
:
"object"
,
"children"
:
{
"depth"
:
{
"type"
:
"number"
,
}
}
}
}
}
}
}
}
,
},
}
,
'title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
]
,
},
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
]
,
'answer'
:
'12
3'
,
'code_language'
:
'python3'
,
'code'
:
code
'title'
:
'123'
,
'variables'
:
[
{
'variable'
:
'args1'
,
'value_selector'
:
[
'1'
,
'123'
,
'args1'
]
,
}
,
{
'variable'
:
'args2'
,
'value_selector'
:
[
'1'
,
'123'
,
'args2'
]
}
],
'answer'
:
'123'
,
'code_language'
:
'python
3'
,
'code'
:
code
}
}
}
)
)
# construct result
result
=
{
...
...
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