Skip to content

Commit cad6b36

Browse files
bowenliang123dengpeng
authored andcommitted
improve: test CodeExecutor with code templates and extract CodeLanguage enum (langgenius#4098)
1 parent 5971c91 commit cad6b36

File tree

7 files changed

+79
-33
lines changed

7 files changed

+79
-33
lines changed

Diff for: api/core/helper/code_executor/code_executor.py

+23-12
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from enum import Enum
12
from typing import Literal, Optional
23

34
from httpx import post
@@ -28,7 +29,25 @@ class Data(BaseModel):
2829
data: Data
2930

3031

32+
class CodeLanguage(str, Enum):
33+
PYTHON3 = 'python3'
34+
JINJA2 = 'jinja2'
35+
JAVASCRIPT = 'javascript'
36+
37+
3138
class CodeExecutor:
39+
code_template_transformers = {
40+
CodeLanguage.PYTHON3: PythonTemplateTransformer,
41+
CodeLanguage.JINJA2: Jinja2TemplateTransformer,
42+
CodeLanguage.JAVASCRIPT: NodeJsTemplateTransformer,
43+
}
44+
45+
code_language_to_running_language = {
46+
CodeLanguage.JAVASCRIPT: 'nodejs',
47+
CodeLanguage.JINJA2: CodeLanguage.PYTHON3,
48+
CodeLanguage.PYTHON3: CodeLanguage.PYTHON3,
49+
}
50+
3251
@classmethod
3352
def execute_code(cls, language: Literal['python3', 'javascript', 'jinja2'], preload: str, code: str) -> str:
3453
"""
@@ -44,9 +63,7 @@ def execute_code(cls, language: Literal['python3', 'javascript', 'jinja2'], prel
4463
}
4564

4665
data = {
47-
'language': 'python3' if language == 'jinja2' else
48-
'nodejs' if language == 'javascript' else
49-
'python3' if language == 'python3' else None,
66+
'language': cls.code_language_to_running_language.get(language),
5067
'code': code,
5168
'preload': preload
5269
}
@@ -86,15 +103,9 @@ def execute_workflow_code_template(cls, language: Literal['python3', 'javascript
86103
:param inputs: inputs
87104
:return:
88105
"""
89-
template_transformer = None
90-
if language == 'python3':
91-
template_transformer = PythonTemplateTransformer
92-
elif language == 'jinja2':
93-
template_transformer = Jinja2TemplateTransformer
94-
elif language == 'javascript':
95-
template_transformer = NodeJsTemplateTransformer
96-
else:
97-
raise CodeExecutionException('Unsupported language')
106+
template_transformer = cls.code_template_transformers.get(language)
107+
if not template_transformer:
108+
raise CodeExecutionException(f'Unsupported language {language}')
98109

99110
runner, preload = template_transformer.transform_caller(code, inputs)
100111

Diff for: api/core/tools/provider/builtin/code/tools/simple_code.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any
22

3-
from core.helper.code_executor.code_executor import CodeExecutor
3+
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
44
from core.tools.entities.tool_entities import ToolInvokeMessage
55
from core.tools.tool.builtin_tool import BuiltinTool
66

@@ -11,10 +11,10 @@ def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMe
1111
invoke simple code
1212
"""
1313

14-
language = tool_parameters.get('language', 'python3')
14+
language = tool_parameters.get('language', CodeLanguage.PYTHON3)
1515
code = tool_parameters.get('code', '')
1616

17-
if language not in ['python3', 'javascript']:
17+
if language not in [CodeLanguage.PYTHON3, CodeLanguage.JAVASCRIPT]:
1818
raise ValueError(f'Only python3 and javascript are supported, not {language}')
1919

2020
result = CodeExecutor.execute_code(language, '', code)

Diff for: api/core/workflow/nodes/code/code_node.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
from typing import Optional, Union, cast
33

4-
from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor
4+
from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor, CodeLanguage
55
from core.workflow.entities.node_entities import NodeRunResult, NodeType
66
from core.workflow.entities.variable_pool import VariablePool
77
from core.workflow.nodes.base_node import BaseNode
@@ -39,7 +39,7 @@ def get_default_config(cls, filters: Optional[dict] = None) -> dict:
3939
:param filters: filter by node config parameters.
4040
:return:
4141
"""
42-
if filters and filters.get("code_language") == "javascript":
42+
if filters and filters.get("code_language") == CodeLanguage.JAVASCRIPT:
4343
return {
4444
"type": "code",
4545
"config": {
@@ -53,7 +53,7 @@ def get_default_config(cls, filters: Optional[dict] = None) -> dict:
5353
"value_selector": []
5454
}
5555
],
56-
"code_language": "javascript",
56+
"code_language": CodeLanguage.JAVASCRIPT,
5757
"code": JAVASCRIPT_DEFAULT_CODE,
5858
"outputs": {
5959
"result": {
@@ -77,7 +77,7 @@ def get_default_config(cls, filters: Optional[dict] = None) -> dict:
7777
"value_selector": []
7878
}
7979
],
80-
"code_language": "python3",
80+
"code_language": CodeLanguage.PYTHON3,
8181
"code": PYTHON_DEFAULT_CODE,
8282
"outputs": {
8383
"result": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import pytest
2+
3+
from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor
4+
5+
CODE_LANGUAGE = 'unsupported_language'
6+
7+
8+
def test_unsupported_with_code_template():
9+
with pytest.raises(CodeExecutionException) as e:
10+
CodeExecutor.execute_workflow_code_template(language=CODE_LANGUAGE, code='', inputs={})
11+
assert str(e.value) == f'Unsupported language {CODE_LANGUAGE}'
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
from core.helper.code_executor.code_executor import CodeExecutor
1+
from textwrap import dedent
22

3-
CODE_LANGUAGE = 'javascript'
3+
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
4+
from core.workflow.nodes.code.code_node import JAVASCRIPT_DEFAULT_CODE
5+
6+
CODE_LANGUAGE = CodeLanguage.JAVASCRIPT
47

58

69
def test_javascript_plain():
@@ -10,9 +13,15 @@ def test_javascript_plain():
1013

1114

1215
def test_javascript_json():
13-
code = """
14-
obj = {'Hello': 'World'}
15-
console.log(JSON.stringify(obj))
16-
"""
16+
code = dedent("""
17+
obj = {'Hello': 'World'}
18+
console.log(JSON.stringify(obj))
19+
""")
1720
result = CodeExecutor.execute_code(language=CODE_LANGUAGE, preload='', code=code)
1821
assert result == '{"Hello":"World"}\n'
22+
23+
24+
def test_javascript_with_code_template():
25+
result = CodeExecutor.execute_workflow_code_template(
26+
language=CODE_LANGUAGE, code=JAVASCRIPT_DEFAULT_CODE, inputs={'arg1': 'Hello', 'arg2': 'World'})
27+
assert result == {'result': 'HelloWorld'}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import base64
22

3-
from core.helper.code_executor.code_executor import CodeExecutor
3+
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
44
from core.helper.code_executor.jinja2_transformer import JINJA2_PRELOAD, PYTHON_RUNNER
55

6-
CODE_LANGUAGE = 'jinja2'
6+
CODE_LANGUAGE = CodeLanguage.JINJA2
77

88

99
def test_jinja2():
@@ -12,3 +12,9 @@ def test_jinja2():
1212
code = PYTHON_RUNNER.replace('{{code}}', template).replace('{{inputs}}', inputs)
1313
result = CodeExecutor.execute_code(language=CODE_LANGUAGE, preload=JINJA2_PRELOAD, code=code)
1414
assert result == '<<RESULT>>Hello World<<RESULT>>\n'
15+
16+
17+
def test_jinja2_with_code_template():
18+
result = CodeExecutor.execute_workflow_code_template(
19+
language=CODE_LANGUAGE, code='Hello {{template}}', inputs={'template': 'World'})
20+
assert result == {'result': 'Hello World'}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
from core.helper.code_executor.code_executor import CodeExecutor
1+
from textwrap import dedent
22

3-
CODE_LANGUAGE = 'python3'
3+
from core.helper.code_executor.code_executor import CodeExecutor, CodeLanguage
4+
from core.workflow.nodes.code.code_node import PYTHON_DEFAULT_CODE
5+
6+
CODE_LANGUAGE = CodeLanguage.PYTHON3
47

58

69
def test_python3_plain():
@@ -10,9 +13,15 @@ def test_python3_plain():
1013

1114

1215
def test_python3_json():
13-
code = """
14-
import json
15-
print(json.dumps({'Hello': 'World'}))
16-
"""
16+
code = dedent("""
17+
import json
18+
print(json.dumps({'Hello': 'World'}))
19+
""")
1720
result = CodeExecutor.execute_code(language=CODE_LANGUAGE, preload='', code=code)
1821
assert result == '{"Hello": "World"}\n'
22+
23+
24+
def test_python3_with_code_template():
25+
result = CodeExecutor.execute_workflow_code_template(
26+
language=CODE_LANGUAGE, code=PYTHON_DEFAULT_CODE, inputs={'arg1': 'Hello', 'arg2': 'World'})
27+
assert result == {'result': 'HelloWorld'}

0 commit comments

Comments
 (0)